mirror of
https://github.com/tsl0922/ttyd.git
synced 2026-01-02 00:44:23 +01:00
src: reformat with clang-format
This commit is contained in:
463
src/http.c
463
src/http.c
@@ -1,282 +1,269 @@
|
||||
#include <string.h>
|
||||
#include <libwebsockets.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <string.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "server.h"
|
||||
#include "html.h"
|
||||
#include "server.h"
|
||||
#include "utils.h"
|
||||
|
||||
enum {
|
||||
AUTH_OK, AUTH_FAIL, AUTH_ERROR
|
||||
};
|
||||
enum { AUTH_OK, AUTH_FAIL, AUTH_ERROR };
|
||||
|
||||
static char * html_cache = NULL;
|
||||
static char *html_cache = NULL;
|
||||
static size_t html_cache_len = 0;
|
||||
|
||||
static int
|
||||
check_auth(struct lws *wsi, struct pss_http *pss) {
|
||||
if (server->credential == NULL)
|
||||
return AUTH_OK;
|
||||
static int check_auth(struct lws *wsi, struct pss_http *pss) {
|
||||
if (server->credential == NULL) return AUTH_OK;
|
||||
|
||||
int hdr_length = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_AUTHORIZATION);
|
||||
char buf[hdr_length + 1];
|
||||
int len = lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_HTTP_AUTHORIZATION);
|
||||
if (len > 0) {
|
||||
// extract base64 text from authorization header
|
||||
char *ptr = &buf[0];
|
||||
char *token, *b64_text = NULL;
|
||||
int i = 1;
|
||||
while ((token = strsep(&ptr, " ")) != NULL) {
|
||||
if (strlen(token) == 0)
|
||||
continue;
|
||||
if (i++ == 2) {
|
||||
b64_text = token;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (b64_text != NULL && !strcmp(b64_text, server->credential))
|
||||
return AUTH_OK;
|
||||
int hdr_length = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_AUTHORIZATION);
|
||||
char buf[hdr_length + 1];
|
||||
int len = lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_HTTP_AUTHORIZATION);
|
||||
if (len > 0) {
|
||||
// extract base64 text from authorization header
|
||||
char *ptr = &buf[0];
|
||||
char *token, *b64_text = NULL;
|
||||
int i = 1;
|
||||
while ((token = strsep(&ptr, " ")) != NULL) {
|
||||
if (strlen(token) == 0) continue;
|
||||
if (i++ == 2) {
|
||||
b64_text = token;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (b64_text != NULL && !strcmp(b64_text, server->credential))
|
||||
return AUTH_OK;
|
||||
}
|
||||
|
||||
unsigned char buffer[1024 + LWS_PRE], *p, *end;
|
||||
p = buffer + LWS_PRE;
|
||||
end = p + sizeof(buffer) - LWS_PRE;
|
||||
unsigned char buffer[1024 + LWS_PRE], *p, *end;
|
||||
p = buffer + LWS_PRE;
|
||||
end = p + sizeof(buffer) - LWS_PRE;
|
||||
|
||||
char *body = strdup("401 Unauthorized\n");
|
||||
size_t n = strlen(body);
|
||||
char *body = strdup("401 Unauthorized\n");
|
||||
size_t n = strlen(body);
|
||||
|
||||
if (lws_add_http_header_status(wsi, HTTP_STATUS_UNAUTHORIZED, &p, end))
|
||||
return AUTH_ERROR;
|
||||
if (lws_add_http_header_by_token(wsi,
|
||||
WSI_TOKEN_HTTP_WWW_AUTHENTICATE,
|
||||
(unsigned char *) "Basic realm=\"ttyd\"",
|
||||
18, &p, end))
|
||||
return AUTH_ERROR;
|
||||
if (lws_add_http_header_content_length(wsi, n, &p, end))
|
||||
return AUTH_ERROR;
|
||||
if (lws_finalize_http_header(wsi, &p, end))
|
||||
return AUTH_ERROR;
|
||||
if (lws_write(wsi, buffer + LWS_PRE, p - (buffer + LWS_PRE), LWS_WRITE_HTTP_HEADERS) < 0)
|
||||
return AUTH_ERROR;
|
||||
if (lws_add_http_header_status(wsi, HTTP_STATUS_UNAUTHORIZED, &p, end))
|
||||
return AUTH_ERROR;
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_WWW_AUTHENTICATE,
|
||||
(unsigned char *)"Basic realm=\"ttyd\"", 18,
|
||||
&p, end))
|
||||
return AUTH_ERROR;
|
||||
if (lws_add_http_header_content_length(wsi, n, &p, end)) return AUTH_ERROR;
|
||||
if (lws_finalize_http_header(wsi, &p, end)) return AUTH_ERROR;
|
||||
if (lws_write(wsi, buffer + LWS_PRE, p - (buffer + LWS_PRE),
|
||||
LWS_WRITE_HTTP_HEADERS) < 0)
|
||||
return AUTH_ERROR;
|
||||
|
||||
pss->buffer = pss->ptr = body;
|
||||
pss->len = n;
|
||||
lws_callback_on_writable(wsi);
|
||||
pss->buffer = pss->ptr = body;
|
||||
pss->len = n;
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
return AUTH_FAIL;
|
||||
return AUTH_FAIL;
|
||||
}
|
||||
|
||||
static bool
|
||||
accept_gzip(struct lws *wsi) {
|
||||
int hdr_length = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING);
|
||||
char buf[hdr_length + 1];
|
||||
int len = lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_HTTP_ACCEPT_ENCODING);
|
||||
return len > 0 && strstr(buf, "gzip") != NULL;
|
||||
static bool accept_gzip(struct lws *wsi) {
|
||||
int hdr_length = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING);
|
||||
char buf[hdr_length + 1];
|
||||
int len = lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_HTTP_ACCEPT_ENCODING);
|
||||
return len > 0 && strstr(buf, "gzip") != NULL;
|
||||
}
|
||||
|
||||
static bool
|
||||
uncompress_html(char **output, size_t *output_len) {
|
||||
if (html_cache == NULL || html_cache_len == 0) {
|
||||
z_stream stream;
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
if (inflateInit2(&stream, 16 + 15) != Z_OK)
|
||||
return false;
|
||||
static bool uncompress_html(char **output, size_t *output_len) {
|
||||
if (html_cache == NULL || html_cache_len == 0) {
|
||||
z_stream stream;
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
if (inflateInit2(&stream, 16 + 15) != Z_OK) return false;
|
||||
|
||||
html_cache_len = index_html_size;
|
||||
html_cache = xmalloc(html_cache_len);
|
||||
html_cache_len = index_html_size;
|
||||
html_cache = xmalloc(html_cache_len);
|
||||
|
||||
stream.avail_in = index_html_len;
|
||||
stream.avail_out = html_cache_len;
|
||||
stream.next_in = (void *) index_html;
|
||||
stream.next_out = (void *) html_cache;
|
||||
stream.avail_in = index_html_len;
|
||||
stream.avail_out = html_cache_len;
|
||||
stream.next_in = (void *)index_html;
|
||||
stream.next_out = (void *)html_cache;
|
||||
|
||||
int ret = inflate(&stream, Z_SYNC_FLUSH);
|
||||
inflateEnd(&stream);
|
||||
if (ret != Z_STREAM_END) {
|
||||
free(html_cache);
|
||||
html_cache = NULL;
|
||||
html_cache_len = 0;
|
||||
return false;
|
||||
}
|
||||
int ret = inflate(&stream, Z_SYNC_FLUSH);
|
||||
inflateEnd(&stream);
|
||||
if (ret != Z_STREAM_END) {
|
||||
free(html_cache);
|
||||
html_cache = NULL;
|
||||
html_cache_len = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
*output = html_cache;
|
||||
*output_len = html_cache_len;
|
||||
*output = html_cache;
|
||||
*output_len = html_cache_len;
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
pss_buffer_free(struct pss_http *pss) {
|
||||
if (pss->buffer != (char *) index_html && pss->buffer != html_cache)
|
||||
free(pss->buffer);
|
||||
static void pss_buffer_free(struct pss_http *pss) {
|
||||
if (pss->buffer != (char *)index_html && pss->buffer != html_cache)
|
||||
free(pss->buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
access_log(struct lws *wsi, const char *path) {
|
||||
char rip[50];
|
||||
static void access_log(struct lws *wsi, const char *path) {
|
||||
char rip[50];
|
||||
|
||||
#if LWS_LIBRARY_VERSION_NUMBER >= 2004000
|
||||
lws_get_peer_simple(lws_get_network_wsi(wsi), rip, sizeof(rip));
|
||||
lws_get_peer_simple(lws_get_network_wsi(wsi), rip, sizeof(rip));
|
||||
#else
|
||||
char name[100];
|
||||
lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), name, sizeof(name), rip, sizeof(rip));
|
||||
char name[100];
|
||||
lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), name, sizeof(name), rip,
|
||||
sizeof(rip));
|
||||
#endif
|
||||
lwsl_notice("HTTP %s - %s\n", path, rip);
|
||||
lwsl_notice("HTTP %s - %s\n", path, rip);
|
||||
}
|
||||
|
||||
int
|
||||
callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
|
||||
struct pss_http *pss = (struct pss_http *) user;
|
||||
unsigned char buffer[4096 + LWS_PRE], *p, *end;
|
||||
char buf[256];
|
||||
bool done = false;
|
||||
int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
||||
void *in, size_t len) {
|
||||
struct pss_http *pss = (struct pss_http *)user;
|
||||
unsigned char buffer[4096 + LWS_PRE], *p, *end;
|
||||
char buf[256];
|
||||
bool done = false;
|
||||
|
||||
switch (reason) {
|
||||
case LWS_CALLBACK_HTTP:
|
||||
access_log(wsi, (const char *) in);
|
||||
snprintf(pss->path, sizeof(pss->path), "%s", (const char *) in);
|
||||
switch (check_auth(wsi, pss)) {
|
||||
case AUTH_OK:
|
||||
break;
|
||||
case AUTH_FAIL:
|
||||
return 0;
|
||||
case AUTH_ERROR:
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
p = buffer + LWS_PRE;
|
||||
end = p + sizeof(buffer) - LWS_PRE;
|
||||
|
||||
if (strcmp(pss->path, endpoints.token) == 0) {
|
||||
const char *credential = server->credential != NULL ? server->credential : "";
|
||||
size_t n = sprintf(buf, "{\"token\": \"%s\"}", credential);
|
||||
if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end))
|
||||
return 1;
|
||||
if (lws_add_http_header_by_token(wsi,
|
||||
WSI_TOKEN_HTTP_CONTENT_TYPE,
|
||||
(unsigned char *) "application/json;charset=utf-8",
|
||||
30, &p, end))
|
||||
return 1;
|
||||
if (lws_add_http_header_content_length(wsi, (unsigned long) n, &p, end))
|
||||
return 1;
|
||||
if (lws_finalize_http_header(wsi, &p, end))
|
||||
return 1;
|
||||
if (lws_write(wsi, buffer + LWS_PRE, p - (buffer + LWS_PRE), LWS_WRITE_HTTP_HEADERS) < 0)
|
||||
return 1;
|
||||
pss->buffer = pss->ptr = strdup(buf);
|
||||
pss->len = n;
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
}
|
||||
|
||||
if (strcmp(pss->path, endpoints.index) != 0) {
|
||||
lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
|
||||
goto try_to_reuse;
|
||||
}
|
||||
|
||||
const char* content_type = "text/html";
|
||||
if (server->index != NULL) {
|
||||
int n = lws_serve_http_file(wsi, server->index, content_type, NULL, 0);
|
||||
if (n < 0 || (n > 0 && lws_http_transaction_completed(wsi)))
|
||||
return 1;
|
||||
} else {
|
||||
char *output = (char *) index_html;
|
||||
size_t output_len = index_html_len;
|
||||
if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end))
|
||||
return 1;
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (const unsigned char *) content_type, 9, &p, end))
|
||||
return 1;
|
||||
#ifdef LWS_WITH_HTTP_STREAM_COMPRESSION
|
||||
if (!uncompress_html(&output, &output_len))
|
||||
return 1;
|
||||
#else
|
||||
if (accept_gzip(wsi)) {
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING, (unsigned char *) "gzip", 4, &p, end))
|
||||
return 1;
|
||||
} else {
|
||||
if (!uncompress_html(&output, &output_len))
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (lws_add_http_header_content_length(wsi, (unsigned long) output_len, &p, end))
|
||||
return 1;
|
||||
|
||||
if (lws_finalize_http_header(wsi, &p, end))
|
||||
return 1;
|
||||
if (lws_write(wsi, buffer + LWS_PRE, p - (buffer + LWS_PRE), LWS_WRITE_HTTP_HEADERS) < 0)
|
||||
return 1;
|
||||
#if LWS_LIBRARY_VERSION_MAJOR < 2
|
||||
if (lws_write_http(wsi, output, output_len) < 0)
|
||||
return 1;
|
||||
goto try_to_reuse;
|
||||
#else
|
||||
pss->buffer = pss->ptr = output;
|
||||
pss->len = output_len;
|
||||
lws_callback_on_writable(wsi);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_HTTP_WRITEABLE:
|
||||
if (!pss->buffer || pss->len <= 0) {
|
||||
goto try_to_reuse;
|
||||
}
|
||||
|
||||
do {
|
||||
int n = sizeof(buffer) - LWS_PRE;
|
||||
int m = lws_get_peer_write_allowance(wsi);
|
||||
if (m == 0) {
|
||||
lws_callback_on_writable(wsi);
|
||||
return 0;
|
||||
} else if (m != -1 && m < n) {
|
||||
n = m;
|
||||
}
|
||||
if (pss->ptr + n > pss->buffer + pss->len) {
|
||||
n = (int) (pss->len - (pss->ptr - pss->buffer));
|
||||
done = true;
|
||||
}
|
||||
memcpy(buffer + LWS_PRE, pss->ptr, n);
|
||||
pss->ptr += n;
|
||||
if (lws_write_http(wsi, buffer + LWS_PRE, (size_t) n) < n) {
|
||||
pss_buffer_free(pss);
|
||||
return -1;
|
||||
}
|
||||
} while (!lws_send_pipe_choked(wsi) && !done);
|
||||
|
||||
if (!done && pss->ptr < pss->buffer + pss->len) {
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
}
|
||||
|
||||
pss_buffer_free(pss);
|
||||
goto try_to_reuse;
|
||||
|
||||
case LWS_CALLBACK_HTTP_FILE_COMPLETION:
|
||||
goto try_to_reuse;
|
||||
|
||||
case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION:
|
||||
if (!len || (SSL_get_verify_result((SSL *) in) != X509_V_OK)) {
|
||||
int err = X509_STORE_CTX_get_error((X509_STORE_CTX *) user);
|
||||
int depth = X509_STORE_CTX_get_error_depth((X509_STORE_CTX *) user);
|
||||
const char *msg = X509_verify_cert_error_string(err);
|
||||
lwsl_err("client certificate verification error: %s (%d), depth: %d\n", msg, err, depth);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
switch (reason) {
|
||||
case LWS_CALLBACK_HTTP:
|
||||
access_log(wsi, (const char *)in);
|
||||
snprintf(pss->path, sizeof(pss->path), "%s", (const char *)in);
|
||||
switch (check_auth(wsi, pss)) {
|
||||
case AUTH_OK:
|
||||
break;
|
||||
case AUTH_FAIL:
|
||||
return 0;
|
||||
case AUTH_ERROR:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
p = buffer + LWS_PRE;
|
||||
end = p + sizeof(buffer) - LWS_PRE;
|
||||
|
||||
/* if we're on HTTP1.1 or 2.0, will keep the idle connection alive */
|
||||
if (strcmp(pss->path, endpoints.token) == 0) {
|
||||
const char *credential =
|
||||
server->credential != NULL ? server->credential : "";
|
||||
size_t n = sprintf(buf, "{\"token\": \"%s\"}", credential);
|
||||
if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end)) return 1;
|
||||
if (lws_add_http_header_by_token(
|
||||
wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
|
||||
(unsigned char *)"application/json;charset=utf-8", 30, &p, end))
|
||||
return 1;
|
||||
if (lws_add_http_header_content_length(wsi, (unsigned long)n, &p, end))
|
||||
return 1;
|
||||
if (lws_finalize_http_header(wsi, &p, end)) return 1;
|
||||
if (lws_write(wsi, buffer + LWS_PRE, p - (buffer + LWS_PRE),
|
||||
LWS_WRITE_HTTP_HEADERS) < 0)
|
||||
return 1;
|
||||
pss->buffer = pss->ptr = strdup(buf);
|
||||
pss->len = n;
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
}
|
||||
|
||||
if (strcmp(pss->path, endpoints.index) != 0) {
|
||||
lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
|
||||
goto try_to_reuse;
|
||||
}
|
||||
|
||||
const char *content_type = "text/html";
|
||||
if (server->index != NULL) {
|
||||
int n = lws_serve_http_file(wsi, server->index, content_type, NULL, 0);
|
||||
if (n < 0 || (n > 0 && lws_http_transaction_completed(wsi))) return 1;
|
||||
} else {
|
||||
char *output = (char *)index_html;
|
||||
size_t output_len = index_html_len;
|
||||
if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end)) return 1;
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
|
||||
(const unsigned char *)content_type, 9,
|
||||
&p, end))
|
||||
return 1;
|
||||
#ifdef LWS_WITH_HTTP_STREAM_COMPRESSION
|
||||
if (!uncompress_html(&output, &output_len)) return 1;
|
||||
#else
|
||||
if (accept_gzip(wsi)) {
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING,
|
||||
(unsigned char *)"gzip", 4, &p, end))
|
||||
return 1;
|
||||
} else {
|
||||
if (!uncompress_html(&output, &output_len)) return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (lws_add_http_header_content_length(wsi, (unsigned long)output_len,
|
||||
&p, end))
|
||||
return 1;
|
||||
|
||||
if (lws_finalize_http_header(wsi, &p, end)) return 1;
|
||||
if (lws_write(wsi, buffer + LWS_PRE, p - (buffer + LWS_PRE),
|
||||
LWS_WRITE_HTTP_HEADERS) < 0)
|
||||
return 1;
|
||||
#if LWS_LIBRARY_VERSION_MAJOR < 2
|
||||
if (lws_write_http(wsi, output, output_len) < 0) return 1;
|
||||
goto try_to_reuse;
|
||||
#else
|
||||
pss->buffer = pss->ptr = output;
|
||||
pss->len = output_len;
|
||||
lws_callback_on_writable(wsi);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_HTTP_WRITEABLE:
|
||||
if (!pss->buffer || pss->len <= 0) {
|
||||
goto try_to_reuse;
|
||||
}
|
||||
|
||||
do {
|
||||
int n = sizeof(buffer) - LWS_PRE;
|
||||
int m = lws_get_peer_write_allowance(wsi);
|
||||
if (m == 0) {
|
||||
lws_callback_on_writable(wsi);
|
||||
return 0;
|
||||
} else if (m != -1 && m < n) {
|
||||
n = m;
|
||||
}
|
||||
if (pss->ptr + n > pss->buffer + pss->len) {
|
||||
n = (int)(pss->len - (pss->ptr - pss->buffer));
|
||||
done = true;
|
||||
}
|
||||
memcpy(buffer + LWS_PRE, pss->ptr, n);
|
||||
pss->ptr += n;
|
||||
if (lws_write_http(wsi, buffer + LWS_PRE, (size_t)n) < n) {
|
||||
pss_buffer_free(pss);
|
||||
return -1;
|
||||
}
|
||||
} while (!lws_send_pipe_choked(wsi) && !done);
|
||||
|
||||
if (!done && pss->ptr < pss->buffer + pss->len) {
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
}
|
||||
|
||||
pss_buffer_free(pss);
|
||||
goto try_to_reuse;
|
||||
|
||||
case LWS_CALLBACK_HTTP_FILE_COMPLETION:
|
||||
goto try_to_reuse;
|
||||
|
||||
case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION:
|
||||
if (!len || (SSL_get_verify_result((SSL *)in) != X509_V_OK)) {
|
||||
int err = X509_STORE_CTX_get_error((X509_STORE_CTX *)user);
|
||||
int depth = X509_STORE_CTX_get_error_depth((X509_STORE_CTX *)user);
|
||||
const char *msg = X509_verify_cert_error_string(err);
|
||||
lwsl_err("client certificate verification error: %s (%d), depth: %d\n",
|
||||
msg, err, depth);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
/* if we're on HTTP1.1 or 2.0, will keep the idle connection alive */
|
||||
try_to_reuse:
|
||||
if (lws_http_transaction_completed(wsi))
|
||||
return -1;
|
||||
if (lws_http_transaction_completed(wsi)) return -1;
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
815
src/protocol.c
815
src/protocol.c
@@ -1,468 +1,459 @@
|
||||
#include <errno.h>
|
||||
#include <json.h>
|
||||
#include <libwebsockets.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <json.h>
|
||||
|
||||
#include "server.h"
|
||||
#include "terminal.h"
|
||||
#include "utils.h"
|
||||
|
||||
// initial message list
|
||||
static char initial_cmds[] = {
|
||||
SET_WINDOW_TITLE,
|
||||
SET_PREFERENCES
|
||||
};
|
||||
static char initial_cmds[] = {SET_WINDOW_TITLE, SET_PREFERENCES};
|
||||
|
||||
static int
|
||||
send_initial_message(struct lws *wsi, int index) {
|
||||
unsigned char message[LWS_PRE + 1 + 4096];
|
||||
unsigned char *p = &message[LWS_PRE];
|
||||
char buffer[128];
|
||||
int n = 0;
|
||||
static int send_initial_message(struct lws *wsi, int index) {
|
||||
unsigned char message[LWS_PRE + 1 + 4096];
|
||||
unsigned char *p = &message[LWS_PRE];
|
||||
char buffer[128];
|
||||
int n = 0;
|
||||
|
||||
char cmd = initial_cmds[index];
|
||||
switch(cmd) {
|
||||
case SET_WINDOW_TITLE:
|
||||
gethostname(buffer, sizeof(buffer) - 1);
|
||||
n = sprintf((char *) p, "%c%s (%s)", cmd, server->command, buffer);
|
||||
break;
|
||||
case SET_PREFERENCES:
|
||||
n = sprintf((char *) p, "%c%s", cmd, server->prefs_json);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
char cmd = initial_cmds[index];
|
||||
switch (cmd) {
|
||||
case SET_WINDOW_TITLE:
|
||||
gethostname(buffer, sizeof(buffer) - 1);
|
||||
n = sprintf((char *)p, "%c%s (%s)", cmd, server->command, buffer);
|
||||
break;
|
||||
case SET_PREFERENCES:
|
||||
n = sprintf((char *)p, "%c%s", cmd, server->prefs_json);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return lws_write(wsi, p, (size_t) n, LWS_WRITE_BINARY);
|
||||
return lws_write(wsi, p, (size_t)n, LWS_WRITE_BINARY);
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_window_size(struct pss_tty *pss, int *cols, int *rows) {
|
||||
char json[pss->len];
|
||||
strncpy(json, pss->buffer + 1, pss->len - 1);
|
||||
json[pss->len-1] = '\0';
|
||||
static bool parse_window_size(struct pss_tty *pss, int *cols, int *rows) {
|
||||
char json[pss->len];
|
||||
strncpy(json, pss->buffer + 1, pss->len - 1);
|
||||
json[pss->len - 1] = '\0';
|
||||
|
||||
json_object *obj = json_tokener_parse(json);
|
||||
struct json_object *o = NULL;
|
||||
json_object *obj = json_tokener_parse(json);
|
||||
struct json_object *o = NULL;
|
||||
|
||||
if (!json_object_object_get_ex(obj, "columns", &o)) {
|
||||
lwsl_err("columns field not exists, json: %s\n", json);
|
||||
return false;
|
||||
}
|
||||
*cols = json_object_get_int(o);
|
||||
if (!json_object_object_get_ex(obj, "rows", &o)) {
|
||||
lwsl_err("rows field not exists, json: %s\n", json);
|
||||
return false;
|
||||
}
|
||||
*rows = json_object_get_int(o);
|
||||
json_object_put(obj);
|
||||
if (!json_object_object_get_ex(obj, "columns", &o)) {
|
||||
lwsl_err("columns field not exists, json: %s\n", json);
|
||||
return false;
|
||||
}
|
||||
*cols = json_object_get_int(o);
|
||||
if (!json_object_object_get_ex(obj, "rows", &o)) {
|
||||
lwsl_err("rows field not exists, json: %s\n", json);
|
||||
return false;
|
||||
}
|
||||
*rows = json_object_get_int(o);
|
||||
json_object_put(obj);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
check_host_origin(struct lws *wsi) {
|
||||
int origin_length = lws_hdr_total_length(wsi, WSI_TOKEN_ORIGIN);
|
||||
char buf[origin_length + 1];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
int len = lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_ORIGIN);
|
||||
if (len <= 0) {
|
||||
return false;
|
||||
static bool check_host_origin(struct lws *wsi) {
|
||||
int origin_length = lws_hdr_total_length(wsi, WSI_TOKEN_ORIGIN);
|
||||
char buf[origin_length + 1];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
int len = lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_ORIGIN);
|
||||
if (len <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *prot, *address, *path;
|
||||
int port;
|
||||
if (lws_parse_uri(buf, &prot, &address, &port, &path)) return false;
|
||||
if (port == 80 || port == 443) {
|
||||
sprintf(buf, "%s", address);
|
||||
} else {
|
||||
sprintf(buf, "%s:%d", address, port);
|
||||
}
|
||||
|
||||
int host_length = lws_hdr_total_length(wsi, WSI_TOKEN_HOST);
|
||||
if (host_length != strlen(buf)) return false;
|
||||
char host_buf[host_length + 1];
|
||||
memset(host_buf, 0, sizeof(host_buf));
|
||||
len = lws_hdr_copy(wsi, host_buf, sizeof(host_buf), WSI_TOKEN_HOST);
|
||||
|
||||
return len > 0 && strcasecmp(buf, host_buf) == 0;
|
||||
}
|
||||
|
||||
static void pty_proc_free(struct pty_proc *proc) {
|
||||
uv_read_stop((uv_stream_t *)&proc->pipe);
|
||||
uv_close((uv_handle_t *)&proc->pipe, NULL);
|
||||
|
||||
close(proc->pty);
|
||||
|
||||
if (proc->pty_buffer != NULL) {
|
||||
free(proc->pty_buffer);
|
||||
proc->pty_buffer = NULL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < proc->argc; i++) {
|
||||
free(proc->args[i]);
|
||||
}
|
||||
|
||||
free(proc);
|
||||
}
|
||||
|
||||
static void alloc_cb(uv_handle_t *handle, size_t suggested_size,
|
||||
uv_buf_t *buf) {
|
||||
buf->base = xmalloc(suggested_size);
|
||||
buf->len = suggested_size;
|
||||
}
|
||||
|
||||
static void read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) {
|
||||
struct pss_tty *pss = (struct pss_tty *)stream->data;
|
||||
struct pty_proc *proc = pss->proc;
|
||||
proc->pty_len = nread;
|
||||
|
||||
uv_read_stop(stream);
|
||||
|
||||
if (nread <= 0) {
|
||||
if (nread == UV_ENOBUFS || nread == 0) return;
|
||||
proc->pty_buffer = NULL;
|
||||
if (nread == UV_EOF)
|
||||
proc->pty_len = 0;
|
||||
else
|
||||
lwsl_err("read_cb: %s\n", uv_err_name(nread));
|
||||
} else {
|
||||
proc->pty_buffer = xmalloc(LWS_PRE + 1 + (size_t)nread);
|
||||
memcpy(proc->pty_buffer + LWS_PRE + 1, buf->base, (size_t)nread);
|
||||
}
|
||||
free(buf->base);
|
||||
|
||||
lws_callback_on_writable(pss->wsi);
|
||||
}
|
||||
|
||||
static void child_cb(uv_signal_t *handle, int signum) {
|
||||
pid_t pid;
|
||||
int stat;
|
||||
|
||||
struct pty_proc *proc;
|
||||
LIST_HEAD(proc, pty_proc) *procs = handle->data;
|
||||
LIST_FOREACH(proc, procs, entry) {
|
||||
do
|
||||
pid = waitpid(proc->pid, &stat, WNOHANG);
|
||||
while (pid == -1 && errno == EINTR);
|
||||
|
||||
if (pid <= 0) continue;
|
||||
|
||||
if (WIFEXITED(stat)) {
|
||||
proc->status = WEXITSTATUS(stat);
|
||||
lwsl_notice("process exited with code %d, pid: %d\n", proc->status,
|
||||
proc->pid);
|
||||
} else if (WIFSIGNALED(stat)) {
|
||||
int sig = WTERMSIG(stat);
|
||||
char sig_name[20];
|
||||
|
||||
proc->status = 128 + sig;
|
||||
get_sig_name(sig, sig_name, sizeof(sig_name));
|
||||
lwsl_notice("process killed with signal %d (%s), pid: %d\n", sig,
|
||||
sig_name, proc->pid);
|
||||
}
|
||||
|
||||
const char *prot, *address, *path;
|
||||
int port;
|
||||
if (lws_parse_uri(buf, &prot, &address, &port, &path))
|
||||
return false;
|
||||
if (port == 80 || port == 443) {
|
||||
sprintf(buf, "%s", address);
|
||||
LIST_REMOVE(proc, entry);
|
||||
if (proc->state == STATE_KILL) {
|
||||
pty_proc_free(proc);
|
||||
} else {
|
||||
sprintf(buf, "%s:%d", address, port);
|
||||
proc->state = STATE_EXIT;
|
||||
}
|
||||
|
||||
int host_length = lws_hdr_total_length(wsi, WSI_TOKEN_HOST);
|
||||
if (host_length != strlen(buf))
|
||||
return false;
|
||||
char host_buf[host_length + 1];
|
||||
memset(host_buf, 0, sizeof(host_buf));
|
||||
len = lws_hdr_copy(wsi, host_buf, sizeof(host_buf), WSI_TOKEN_HOST);
|
||||
|
||||
return len > 0 && strcasecmp(buf, host_buf) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pty_proc_free(struct pty_proc *proc) {
|
||||
uv_read_stop((uv_stream_t *) &proc->pipe);
|
||||
uv_close((uv_handle_t*) &proc->pipe, NULL);
|
||||
static int spawn_process(struct pss_tty *pss) {
|
||||
struct pty_proc *proc = pss->proc;
|
||||
// append url args to arguments
|
||||
char *argv[server->argc + proc->argc + 1];
|
||||
int i, n = 0;
|
||||
for (i = 0; i < server->argc; i++) {
|
||||
argv[n++] = server->argv[i];
|
||||
}
|
||||
for (i = 0; i < proc->argc; i++) {
|
||||
argv[n++] = proc->args[i];
|
||||
}
|
||||
argv[n] = NULL;
|
||||
|
||||
close(proc->pty);
|
||||
uv_signal_start(&server->watcher, child_cb, SIGCHLD);
|
||||
|
||||
if (proc->pty_buffer != NULL) {
|
||||
free(proc->pty_buffer);
|
||||
proc->pty_buffer = NULL;
|
||||
}
|
||||
// ensure the lws socket fd close-on-exec
|
||||
fd_set_cloexec(lws_get_socket_fd(pss->wsi));
|
||||
|
||||
for (int i = 0; i < proc->argc; i++) {
|
||||
free(proc->args[i]);
|
||||
}
|
||||
// create process with pseudo-tty
|
||||
proc->pid = pty_fork(&proc->pty, argv[0], argv, server->terminal_type);
|
||||
if (proc->pid < 0) {
|
||||
lwsl_err("pty_fork: %d (%s)\n", errno, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
free(proc);
|
||||
lwsl_notice("started process, pid: %d\n", proc->pid);
|
||||
|
||||
proc->pipe.data = pss;
|
||||
uv_pipe_open(&proc->pipe, proc->pty);
|
||||
|
||||
lws_callback_on_writable(pss->wsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
|
||||
buf->base = xmalloc(suggested_size);
|
||||
buf->len = suggested_size;
|
||||
static void kill_process(struct pty_proc *proc) {
|
||||
if (proc->pid <= 0) return;
|
||||
|
||||
pid_t pid = proc->pid;
|
||||
int sig = server->sig_code;
|
||||
char *sig_name = server->sig_name;
|
||||
|
||||
lwsl_notice("killing process %d with signal: %d (%s)\n", pid, sig, sig_name);
|
||||
int pgid = getpgid(pid);
|
||||
if (uv_kill(pgid > 0 ? -pgid : pid, sig) != 0) {
|
||||
lwsl_err("kill: %d, errno: %d (%s)\n", pid, errno, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
|
||||
struct pss_tty *pss = (struct pss_tty *) stream->data;
|
||||
struct pty_proc *proc = pss->proc;
|
||||
proc->pty_len = nread;
|
||||
|
||||
uv_read_stop(stream);
|
||||
|
||||
if (nread <= 0) {
|
||||
if (nread == UV_ENOBUFS || nread == 0)
|
||||
return;
|
||||
proc->pty_buffer = NULL;
|
||||
if (nread == UV_EOF)
|
||||
proc->pty_len = 0;
|
||||
else
|
||||
lwsl_err("read_cb: %s\n", uv_err_name(nread));
|
||||
} else {
|
||||
proc->pty_buffer = xmalloc(LWS_PRE + 1 + (size_t ) nread);
|
||||
memcpy(proc->pty_buffer + LWS_PRE + 1, buf->base, (size_t ) nread);
|
||||
}
|
||||
free(buf->base);
|
||||
|
||||
lws_callback_on_writable(pss->wsi);
|
||||
static void write_cb(uv_write_t *req, int status) {
|
||||
if (status != 0) lwsl_warn("uv_write callback returned status: %d\n", status);
|
||||
free(req->data);
|
||||
free(req);
|
||||
}
|
||||
|
||||
static void
|
||||
child_cb(uv_signal_t *handle, int signum) {
|
||||
pid_t pid;
|
||||
int stat;
|
||||
int callback_tty(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
||||
void *in, size_t len) {
|
||||
struct pss_tty *pss = (struct pss_tty *)user;
|
||||
struct pty_proc *proc;
|
||||
char buf[256];
|
||||
size_t n = 0;
|
||||
|
||||
struct pty_proc *proc;
|
||||
LIST_HEAD(proc, pty_proc) *procs = handle->data;
|
||||
LIST_FOREACH(proc, procs, entry) {
|
||||
do
|
||||
pid = waitpid(proc->pid, &stat, WNOHANG);
|
||||
while (pid == -1 && errno == EINTR);
|
||||
|
||||
if (pid <= 0)
|
||||
continue;
|
||||
|
||||
if (WIFEXITED(stat)) {
|
||||
proc->status = WEXITSTATUS(stat);
|
||||
lwsl_notice("process exited with code %d, pid: %d\n", proc->status, proc->pid);
|
||||
} else if (WIFSIGNALED(stat)) {
|
||||
int sig = WTERMSIG(stat);
|
||||
char sig_name[20];
|
||||
|
||||
proc->status = 128 + sig;
|
||||
get_sig_name(sig, sig_name, sizeof(sig_name));
|
||||
lwsl_notice("process killed with signal %d (%s), pid: %d\n", sig, sig_name, proc->pid);
|
||||
}
|
||||
|
||||
LIST_REMOVE(proc, entry);
|
||||
if (proc->state == STATE_KILL) {
|
||||
pty_proc_free(proc);
|
||||
} else {
|
||||
proc->state = STATE_EXIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
spawn_process(struct pss_tty *pss) {
|
||||
struct pty_proc *proc = pss->proc;
|
||||
// append url args to arguments
|
||||
char *argv[server->argc + proc->argc + 1];
|
||||
int i, n = 0;
|
||||
for (i = 0; i < server->argc; i++) {
|
||||
argv[n++] = server->argv[i];
|
||||
}
|
||||
for (i = 0; i < proc->argc; i++) {
|
||||
argv[n++] = proc->args[i];
|
||||
}
|
||||
argv[n] = NULL;
|
||||
|
||||
uv_signal_start(&server->watcher, child_cb, SIGCHLD);
|
||||
|
||||
// ensure the lws socket fd close-on-exec
|
||||
fd_set_cloexec(lws_get_socket_fd(pss->wsi));
|
||||
|
||||
// create process with pseudo-tty
|
||||
proc->pid = pty_fork(&proc->pty, argv[0], argv, server->terminal_type);
|
||||
if (proc->pid < 0) {
|
||||
lwsl_err("pty_fork: %d (%s)\n", errno, strerror(errno));
|
||||
switch (reason) {
|
||||
case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
|
||||
if (server->once && server->client_count > 0) {
|
||||
lwsl_warn("refuse to serve WS client due to the --once option.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (server->max_clients > 0 &&
|
||||
server->client_count == server->max_clients) {
|
||||
lwsl_warn(
|
||||
"refuse to serve WS client due to the --max-clients option.\n");
|
||||
return 1;
|
||||
}
|
||||
if (lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_GET_URI) <= 0 ||
|
||||
strcmp(buf, endpoints.ws) != 0) {
|
||||
lwsl_warn("refuse to serve WS client for illegal ws path: %s\n", buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
lwsl_notice("started process, pid: %d\n", proc->pid);
|
||||
if (server->check_origin && !check_host_origin(wsi)) {
|
||||
lwsl_warn(
|
||||
"refuse to serve WS client from different origin due to the "
|
||||
"--check-origin option.\n");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
|
||||
proc->pipe.data = pss;
|
||||
uv_pipe_open(&proc->pipe, proc->pty);
|
||||
case LWS_CALLBACK_ESTABLISHED:
|
||||
pss->initialized = false;
|
||||
pss->initial_cmd_index = 0;
|
||||
pss->authenticated = false;
|
||||
pss->wsi = wsi;
|
||||
pss->buffer = NULL;
|
||||
|
||||
lws_callback_on_writable(pss->wsi);
|
||||
pss->proc = proc = xmalloc(sizeof(struct pty_proc));
|
||||
memset(proc, 0, sizeof(struct pty_proc));
|
||||
proc->status = -1;
|
||||
proc->state = STATE_INIT;
|
||||
uv_pipe_init(server->loop, &proc->pipe, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
if (server->url_arg) {
|
||||
while (lws_hdr_copy_fragment(wsi, buf, sizeof(buf),
|
||||
WSI_TOKEN_HTTP_URI_ARGS, n++) > 0) {
|
||||
if (strncmp(buf, "arg=", 4) == 0) {
|
||||
proc->args =
|
||||
xrealloc(proc->args, (proc->argc + 1) * sizeof(char *));
|
||||
proc->args[proc->argc] = strdup(&buf[4]);
|
||||
proc->argc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
kill_process(struct pty_proc *proc) {
|
||||
if (proc->pid <= 0) return;
|
||||
LIST_INSERT_HEAD(&server->procs, proc, entry);
|
||||
server->client_count++;
|
||||
|
||||
pid_t pid = proc->pid;
|
||||
int sig = server->sig_code;
|
||||
char *sig_name = server->sig_name;
|
||||
|
||||
lwsl_notice("killing process %d with signal: %d (%s)\n", pid, sig, sig_name);
|
||||
int pgid = getpgid(pid);
|
||||
if (uv_kill(pgid > 0 ? -pgid : pid, sig) != 0) {
|
||||
lwsl_err("kill: %d, errno: %d (%s)\n", pid, errno, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
write_cb(uv_write_t* req, int status) {
|
||||
if (status != 0)
|
||||
lwsl_warn("uv_write callback returned status: %d\n", status);
|
||||
free(req->data);
|
||||
free(req);
|
||||
}
|
||||
|
||||
int
|
||||
callback_tty(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len) {
|
||||
struct pss_tty *pss = (struct pss_tty *) user;
|
||||
struct pty_proc *proc;
|
||||
char buf[256];
|
||||
size_t n = 0;
|
||||
|
||||
switch (reason) {
|
||||
case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
|
||||
if (server->once && server->client_count > 0) {
|
||||
lwsl_warn("refuse to serve WS client due to the --once option.\n");
|
||||
return 1;
|
||||
}
|
||||
if (server->max_clients > 0 && server->client_count == server->max_clients) {
|
||||
lwsl_warn("refuse to serve WS client due to the --max-clients option.\n");
|
||||
return 1;
|
||||
}
|
||||
if (lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_GET_URI) <= 0 || strcmp(buf, endpoints.ws) != 0) {
|
||||
lwsl_warn("refuse to serve WS client for illegal ws path: %s\n", buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (server->check_origin && !check_host_origin(wsi)) {
|
||||
lwsl_warn("refuse to serve WS client from different origin due to the --check-origin option.\n");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_ESTABLISHED:
|
||||
pss->initialized = false;
|
||||
pss->initial_cmd_index = 0;
|
||||
pss->authenticated = false;
|
||||
pss->wsi = wsi;
|
||||
pss->buffer = NULL;
|
||||
|
||||
pss->proc = proc = xmalloc(sizeof(struct pty_proc));
|
||||
memset(proc, 0, sizeof(struct pty_proc));
|
||||
proc->status = -1;
|
||||
proc->state = STATE_INIT;
|
||||
uv_pipe_init(server->loop, &proc->pipe, 0);
|
||||
|
||||
if (server->url_arg) {
|
||||
while (lws_hdr_copy_fragment(wsi, buf, sizeof(buf), WSI_TOKEN_HTTP_URI_ARGS, n++) > 0) {
|
||||
if (strncmp(buf, "arg=", 4) == 0) {
|
||||
proc->args = xrealloc(proc->args, (proc->argc + 1) * sizeof(char *));
|
||||
proc->args[proc->argc] = strdup(&buf[4]);
|
||||
proc->argc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LIST_INSERT_HEAD(&server->procs, proc, entry);
|
||||
server->client_count++;
|
||||
|
||||
lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_GET_URI);
|
||||
lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_GET_URI);
|
||||
|
||||
#if LWS_LIBRARY_VERSION_NUMBER >= 2004000
|
||||
lws_get_peer_simple(lws_get_network_wsi(wsi), pss->address, sizeof(pss->address));
|
||||
lws_get_peer_simple(lws_get_network_wsi(wsi), pss->address,
|
||||
sizeof(pss->address));
|
||||
#else
|
||||
char name[100];
|
||||
lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), name, sizeof(name), pss->address, sizeof(pss->address));
|
||||
char name[100];
|
||||
lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), name, sizeof(name),
|
||||
pss->address, sizeof(pss->address));
|
||||
#endif
|
||||
lwsl_notice("WS %s - %s, clients: %d\n", buf, pss->address, server->client_count);
|
||||
break;
|
||||
lwsl_notice("WS %s - %s, clients: %d\n", buf, pss->address,
|
||||
server->client_count);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_SERVER_WRITEABLE:
|
||||
proc = pss->proc;
|
||||
if (!pss->initialized) {
|
||||
if (pss->initial_cmd_index == sizeof(initial_cmds)) {
|
||||
pss->initialized = true;
|
||||
uv_read_start((uv_stream_t *)& proc->pipe, alloc_cb, read_cb);
|
||||
break;
|
||||
}
|
||||
if (send_initial_message(wsi, pss->initial_cmd_index) < 0) {
|
||||
lwsl_err("failed to send initial message, index: %d\n", pss->initial_cmd_index);
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_UNEXPECTED_CONDITION, NULL, 0);
|
||||
return -1;
|
||||
}
|
||||
pss->initial_cmd_index++;
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
case LWS_CALLBACK_SERVER_WRITEABLE:
|
||||
proc = pss->proc;
|
||||
if (!pss->initialized) {
|
||||
if (pss->initial_cmd_index == sizeof(initial_cmds)) {
|
||||
pss->initialized = true;
|
||||
uv_read_start((uv_stream_t *)&proc->pipe, alloc_cb, read_cb);
|
||||
break;
|
||||
}
|
||||
if (send_initial_message(wsi, pss->initial_cmd_index) < 0) {
|
||||
lwsl_err("failed to send initial message, index: %d\n",
|
||||
pss->initial_cmd_index);
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_UNEXPECTED_CONDITION, NULL, 0);
|
||||
return -1;
|
||||
}
|
||||
pss->initial_cmd_index++;
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
}
|
||||
|
||||
// read error or client exited, close connection
|
||||
if (proc->status == 0 || proc->pty_len == 0) {
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_NORMAL, NULL, 0);
|
||||
return 1;
|
||||
} else if (proc->status > 0 || proc->pty_len < 0) {
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_UNEXPECTED_CONDITION, NULL, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (proc->pty_buffer == NULL) break;
|
||||
|
||||
proc->pty_buffer[LWS_PRE] = OUTPUT;
|
||||
n = (size_t)(proc->pty_len + 1);
|
||||
if (lws_write(wsi, (unsigned char *)proc->pty_buffer + LWS_PRE, n,
|
||||
LWS_WRITE_BINARY) < n) {
|
||||
lwsl_err("write OUTPUT to WS\n");
|
||||
}
|
||||
free(proc->pty_buffer);
|
||||
proc->pty_buffer = NULL;
|
||||
uv_read_start((uv_stream_t *)&proc->pipe, alloc_cb, read_cb);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RECEIVE:
|
||||
if (pss->buffer == NULL) {
|
||||
pss->buffer = xmalloc(len);
|
||||
pss->len = len;
|
||||
memcpy(pss->buffer, in, len);
|
||||
} else {
|
||||
pss->buffer = xrealloc(pss->buffer, pss->len + len);
|
||||
memcpy(pss->buffer + pss->len, in, len);
|
||||
pss->len += len;
|
||||
}
|
||||
|
||||
const char command = pss->buffer[0];
|
||||
|
||||
// check auth
|
||||
if (server->credential != NULL && !pss->authenticated &&
|
||||
command != JSON_DATA) {
|
||||
lwsl_warn("WS client not authenticated\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// check if there are more fragmented messages
|
||||
if (lws_remaining_packet_payload(wsi) > 0 ||
|
||||
!lws_is_final_fragment(wsi)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
proc = pss->proc;
|
||||
switch (command) {
|
||||
case INPUT:
|
||||
if (proc->pty == 0) break;
|
||||
if (server->readonly) return 0;
|
||||
|
||||
char *data = xmalloc(pss->len - 1);
|
||||
memcpy(data, pss->buffer + 1, pss->len - 1);
|
||||
|
||||
uv_buf_t b = {data, pss->len - 1};
|
||||
uv_write_t *req = xmalloc(sizeof(uv_write_t));
|
||||
req->data = data;
|
||||
|
||||
int err = uv_write(req, (uv_stream_t *)&proc->pipe, &b, 1, write_cb);
|
||||
if (err) {
|
||||
lwsl_err("uv_write: %s\n", uv_err_name(err));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case RESIZE_TERMINAL: {
|
||||
int cols, rows;
|
||||
if (parse_window_size(pss, &cols, &rows)) {
|
||||
if (pty_resize(proc->pty, cols, rows) < 0) {
|
||||
lwsl_err("pty_resize: %d (%s)\n", errno, strerror(errno));
|
||||
}
|
||||
|
||||
// read error or client exited, close connection
|
||||
if (proc->status == 0 || proc->pty_len == 0) {
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_NORMAL, NULL, 0);
|
||||
return 1;
|
||||
} else if (proc->status > 0 || proc->pty_len < 0) {
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_UNEXPECTED_CONDITION, NULL, 0);
|
||||
return -1;
|
||||
}
|
||||
} break;
|
||||
case JSON_DATA:
|
||||
if (proc->pid > 0) break;
|
||||
if (server->credential != NULL) {
|
||||
json_object *obj = json_tokener_parse(pss->buffer);
|
||||
struct json_object *o = NULL;
|
||||
if (json_object_object_get_ex(obj, "AuthToken", &o)) {
|
||||
const char *token = json_object_get_string(o);
|
||||
if (token != NULL && !strcmp(token, server->credential))
|
||||
pss->authenticated = true;
|
||||
else
|
||||
lwsl_warn("WS authentication failed with token: %s\n", token);
|
||||
}
|
||||
|
||||
if (proc->pty_buffer == NULL)
|
||||
break;
|
||||
|
||||
proc->pty_buffer[LWS_PRE] = OUTPUT;
|
||||
n = (size_t) (proc->pty_len + 1);
|
||||
if (lws_write(wsi, (unsigned char *) proc->pty_buffer + LWS_PRE, n, LWS_WRITE_BINARY) < n) {
|
||||
lwsl_err("write OUTPUT to WS\n");
|
||||
if (!pss->authenticated) {
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_POLICY_VIOLATION, NULL, 0);
|
||||
return -1;
|
||||
}
|
||||
free(proc->pty_buffer);
|
||||
proc->pty_buffer = NULL;
|
||||
uv_read_start((uv_stream_t *)& proc->pipe, alloc_cb, read_cb);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RECEIVE:
|
||||
if (pss->buffer == NULL) {
|
||||
pss->buffer = xmalloc(len);
|
||||
pss->len = len;
|
||||
memcpy(pss->buffer, in, len);
|
||||
} else {
|
||||
pss->buffer = xrealloc(pss->buffer, pss->len + len);
|
||||
memcpy(pss->buffer + pss->len, in, len);
|
||||
pss->len += len;
|
||||
}
|
||||
|
||||
const char command = pss->buffer[0];
|
||||
|
||||
// check auth
|
||||
if (server->credential != NULL && !pss->authenticated && command != JSON_DATA) {
|
||||
lwsl_warn("WS client not authenticated\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// check if there are more fragmented messages
|
||||
if (lws_remaining_packet_payload(wsi) > 0 || !lws_is_final_fragment(wsi)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
proc = pss->proc;
|
||||
switch (command) {
|
||||
case INPUT:
|
||||
if (proc->pty == 0)
|
||||
break;
|
||||
if (server->readonly)
|
||||
return 0;
|
||||
|
||||
char *data = xmalloc(pss->len - 1);
|
||||
memcpy(data, pss->buffer + 1, pss->len - 1);
|
||||
|
||||
uv_buf_t b = { data, pss->len - 1 };
|
||||
uv_write_t *req = xmalloc(sizeof(uv_write_t));
|
||||
req->data = data;
|
||||
|
||||
int err = uv_write(req, (uv_stream_t *) &proc->pipe, &b, 1, write_cb);
|
||||
if (err) {
|
||||
lwsl_err("uv_write: %s\n", uv_err_name(err));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case RESIZE_TERMINAL:
|
||||
{
|
||||
int cols, rows;
|
||||
if (parse_window_size(pss, &cols, &rows)) {
|
||||
if (pty_resize(proc->pty, cols, rows) < 0) {
|
||||
lwsl_err("pty_resize: %d (%s)\n", errno, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JSON_DATA:
|
||||
if (proc->pid > 0)
|
||||
break;
|
||||
if (server->credential != NULL) {
|
||||
json_object *obj = json_tokener_parse(pss->buffer);
|
||||
struct json_object *o = NULL;
|
||||
if (json_object_object_get_ex(obj, "AuthToken", &o)) {
|
||||
const char *token = json_object_get_string(o);
|
||||
if (token != NULL && !strcmp(token, server->credential))
|
||||
pss->authenticated = true;
|
||||
else
|
||||
lwsl_warn("WS authentication failed with token: %s\n", token);
|
||||
}
|
||||
if (!pss->authenticated) {
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_POLICY_VIOLATION, NULL, 0);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (spawn_process(pss) != 0) return 1;
|
||||
break;
|
||||
default:
|
||||
lwsl_warn("ignored unknown message type: %c\n", command);
|
||||
break;
|
||||
}
|
||||
|
||||
if (pss->buffer != NULL) {
|
||||
free(pss->buffer);
|
||||
pss->buffer = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLOSED:
|
||||
if (pss->wsi == NULL)
|
||||
break;
|
||||
|
||||
server->client_count--;
|
||||
lwsl_notice("WS closed from %s, clients: %d\n", pss->address, server->client_count);
|
||||
if (pss->buffer != NULL) {
|
||||
free(pss->buffer);
|
||||
}
|
||||
|
||||
proc = pss->proc;
|
||||
if (proc->state == STATE_EXIT) {
|
||||
pty_proc_free(proc);
|
||||
} else {
|
||||
proc->state = STATE_KILL;
|
||||
uv_read_stop((uv_stream_t *) &proc->pipe);
|
||||
kill_process(proc);
|
||||
}
|
||||
|
||||
if (server->once && server->client_count == 0) {
|
||||
lwsl_notice("exiting due to the --once option.\n");
|
||||
force_exit = true;
|
||||
lws_cancel_service(context);
|
||||
exit(0);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
if (spawn_process(pss) != 0) return 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
lwsl_warn("ignored unknown message type: %c\n", command);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (pss->buffer != NULL) {
|
||||
free(pss->buffer);
|
||||
pss->buffer = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLOSED:
|
||||
if (pss->wsi == NULL) break;
|
||||
|
||||
server->client_count--;
|
||||
lwsl_notice("WS closed from %s, clients: %d\n", pss->address,
|
||||
server->client_count);
|
||||
if (pss->buffer != NULL) {
|
||||
free(pss->buffer);
|
||||
}
|
||||
|
||||
proc = pss->proc;
|
||||
if (proc->state == STATE_EXIT) {
|
||||
pty_proc_free(proc);
|
||||
} else {
|
||||
proc->state = STATE_KILL;
|
||||
uv_read_stop((uv_stream_t *)&proc->pipe);
|
||||
kill_process(proc);
|
||||
}
|
||||
|
||||
if (server->once && server->client_count == 0) {
|
||||
lwsl_notice("exiting due to the --once option.\n");
|
||||
force_exit = true;
|
||||
lws_cancel_service(context);
|
||||
exit(0);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
910
src/server.c
910
src/server.c
File diff suppressed because it is too large
Load Diff
100
src/server.h
100
src/server.h
@@ -15,9 +15,9 @@
|
||||
|
||||
// url paths
|
||||
struct endpoints {
|
||||
char *ws;
|
||||
char *index;
|
||||
char *token;
|
||||
char *ws;
|
||||
char *index;
|
||||
char *token;
|
||||
};
|
||||
|
||||
extern volatile bool force_exit;
|
||||
@@ -25,74 +25,66 @@ extern struct lws_context *context;
|
||||
extern struct server *server;
|
||||
extern struct endpoints endpoints;
|
||||
|
||||
typedef enum {
|
||||
STATE_INIT, STATE_KILL, STATE_EXIT
|
||||
} proc_state;
|
||||
typedef enum { STATE_INIT, STATE_KILL, STATE_EXIT } proc_state;
|
||||
|
||||
struct pss_http {
|
||||
char path[128];
|
||||
char *buffer;
|
||||
char *ptr;
|
||||
size_t len;
|
||||
char path[128];
|
||||
char *buffer;
|
||||
char *ptr;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
struct pty_proc {
|
||||
char **args;
|
||||
int argc;
|
||||
char **args;
|
||||
int argc;
|
||||
|
||||
pid_t pid;
|
||||
int status;
|
||||
proc_state state;
|
||||
pid_t pid;
|
||||
int status;
|
||||
proc_state state;
|
||||
|
||||
int pty;
|
||||
char *pty_buffer;
|
||||
ssize_t pty_len;
|
||||
int pty;
|
||||
char *pty_buffer;
|
||||
ssize_t pty_len;
|
||||
|
||||
uv_pipe_t pipe;
|
||||
uv_pipe_t pipe;
|
||||
|
||||
LIST_ENTRY(pty_proc) entry;
|
||||
LIST_ENTRY(pty_proc) entry;
|
||||
};
|
||||
|
||||
struct pss_tty {
|
||||
bool initialized;
|
||||
int initial_cmd_index;
|
||||
bool authenticated;
|
||||
char address[50];
|
||||
bool initialized;
|
||||
int initial_cmd_index;
|
||||
bool authenticated;
|
||||
char address[50];
|
||||
|
||||
struct lws *wsi;
|
||||
char *buffer;
|
||||
size_t len;
|
||||
struct lws *wsi;
|
||||
char *buffer;
|
||||
size_t len;
|
||||
|
||||
struct pty_proc *proc;
|
||||
struct pty_proc *proc;
|
||||
};
|
||||
|
||||
struct server {
|
||||
int client_count; // client count
|
||||
char *prefs_json; // client preferences
|
||||
char *credential; // encoded basic auth credential
|
||||
char *index; // custom index.html
|
||||
char *command; // full command line
|
||||
char **argv; // command with arguments
|
||||
int argc; // command + arguments count
|
||||
int sig_code; // close signal
|
||||
char sig_name[20]; // human readable signal string
|
||||
bool url_arg; // allow client to send cli arguments in URL
|
||||
bool readonly; // whether not allow clients to write to the TTY
|
||||
bool check_origin; // whether allow websocket connection from different origin
|
||||
int max_clients; // maximum clients to support
|
||||
bool once; // whether accept only one client and exit on disconnection
|
||||
char socket_path[255]; // UNIX domain socket path
|
||||
char terminal_type[30]; // terminal type to report
|
||||
int client_count; // client count
|
||||
char *prefs_json; // client preferences
|
||||
char *credential; // encoded basic auth credential
|
||||
char *index; // custom index.html
|
||||
char *command; // full command line
|
||||
char **argv; // command with arguments
|
||||
int argc; // command + arguments count
|
||||
int sig_code; // close signal
|
||||
char sig_name[20]; // human readable signal string
|
||||
bool url_arg; // allow client to send cli arguments in URL
|
||||
bool readonly; // whether not allow clients to write to the TTY
|
||||
bool
|
||||
check_origin; // whether allow websocket connection from different origin
|
||||
int max_clients; // maximum clients to support
|
||||
bool once; // whether accept only one client and exit on disconnection
|
||||
char socket_path[255]; // UNIX domain socket path
|
||||
char terminal_type[30]; // terminal type to report
|
||||
|
||||
uv_loop_t *loop; // the libuv event loop
|
||||
uv_signal_t watcher; // SIGCHLD watcher
|
||||
uv_loop_t *loop; // the libuv event loop
|
||||
uv_signal_t watcher; // SIGCHLD watcher
|
||||
|
||||
LIST_HEAD(proc, pty_proc) procs; // started process list
|
||||
LIST_HEAD(proc, pty_proc) procs; // started process list
|
||||
};
|
||||
|
||||
extern int
|
||||
callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len);
|
||||
|
||||
extern int
|
||||
callback_tty(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len);
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(__OpenBSD__) || defined(__APPLE__)
|
||||
#include <util.h>
|
||||
@@ -16,40 +16,39 @@
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
pid_t
|
||||
pty_fork(int *pty, const char *file, char *const argv[], const char *term) {
|
||||
pid_t pid = forkpty(pty, NULL, NULL, NULL);
|
||||
|
||||
if (pid < 0) {
|
||||
return pid;
|
||||
} else if (pid == 0) {
|
||||
setenv("TERM", term, true);
|
||||
int ret = execvp(file, argv);
|
||||
if (ret < 0) {
|
||||
perror("execvp failed\n");
|
||||
_exit(-errno);
|
||||
}
|
||||
}
|
||||
|
||||
// set the file descriptor non blocking
|
||||
int flags = fcntl(*pty, F_GETFL);
|
||||
if (flags != -1) {
|
||||
fcntl(*pty, F_SETFD, flags | O_NONBLOCK);
|
||||
}
|
||||
// set the file descriptor close-on-exec
|
||||
fd_set_cloexec(*pty);
|
||||
pid_t pty_fork(int *pty, const char *file, char *const argv[],
|
||||
const char *term) {
|
||||
pid_t pid = forkpty(pty, NULL, NULL, NULL);
|
||||
|
||||
if (pid < 0) {
|
||||
return pid;
|
||||
} else if (pid == 0) {
|
||||
setenv("TERM", term, true);
|
||||
int ret = execvp(file, argv);
|
||||
if (ret < 0) {
|
||||
perror("execvp failed\n");
|
||||
_exit(-errno);
|
||||
}
|
||||
}
|
||||
|
||||
// set the file descriptor non blocking
|
||||
int flags = fcntl(*pty, F_GETFL);
|
||||
if (flags != -1) {
|
||||
fcntl(*pty, F_SETFD, flags | O_NONBLOCK);
|
||||
}
|
||||
// set the file descriptor close-on-exec
|
||||
fd_set_cloexec(*pty);
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
int
|
||||
pty_resize(pid_t pty, int cols, int rows) {
|
||||
struct winsize size;
|
||||
int pty_resize(pid_t pty, int cols, int rows) {
|
||||
struct winsize size;
|
||||
|
||||
size.ws_col = (unsigned short) cols;
|
||||
size.ws_row = (unsigned short) rows;
|
||||
size.ws_xpixel = 0;
|
||||
size.ws_ypixel = 0;
|
||||
size.ws_col = (unsigned short)cols;
|
||||
size.ws_row = (unsigned short)rows;
|
||||
size.ws_xpixel = 0;
|
||||
size.ws_ypixel = 0;
|
||||
|
||||
return ioctl(pty, TIOCSWINSZ, &size);
|
||||
return ioctl(pty, TIOCSWINSZ, &size);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
#ifndef TTYD_TERMINAL_H
|
||||
#define TTYD_TERMINAL_H
|
||||
|
||||
int
|
||||
pty_fork(int *pty, const char *file, char *const argv[], const char *term);
|
||||
int pty_fork(int *pty, const char *file, char *const argv[], const char *term);
|
||||
|
||||
int
|
||||
pty_resize(int pty, int cols, int rows);
|
||||
int pty_resize(int pty, int cols, int rows);
|
||||
|
||||
#endif //TTYD_TERMINAL_H
|
||||
#endif // TTYD_TERMINAL_H
|
||||
|
||||
176
src/utils.c
176
src/utils.c
@@ -1,138 +1,124 @@
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#if defined(__linux__) && !defined(__ANDROID__)
|
||||
// https://github.com/karelzak/util-linux/blob/master/misc-utils/kill.c
|
||||
const char *sys_signame[NSIG] = {
|
||||
"zero", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "UNUSED",
|
||||
"FPE", "KILL", "USR1", "SEGV", "USR2", "PIPE", "ALRM", "TERM",
|
||||
"STKFLT","CHLD", "CONT", "STOP", "TSTP", "TTIN", "TTOU", "URG",
|
||||
"XCPU", "XFSZ", "VTALRM","PROF", "WINCH", "IO", "PWR", "SYS", NULL
|
||||
};
|
||||
"zero", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "UNUSED", "FPE",
|
||||
"KILL", "USR1", "SEGV", "USR2", "PIPE", "ALRM", "TERM", "STKFLT", "CHLD",
|
||||
"CONT", "STOP", "TSTP", "TTIN", "TTOU", "URG", "XCPU", "XFSZ", "VTALRM",
|
||||
"PROF", "WINCH", "IO", "PWR", "SYS", NULL};
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
#include <windows.h>
|
||||
// https://github.com/mirror/newlib-cygwin/blob/master/winsup/cygwin/strsig.cc
|
||||
#ifndef NSIG
|
||||
#define NSIG 33
|
||||
#endif
|
||||
const char *sys_signame[NSIG] = {
|
||||
"zero", "HUP", "INT", "QUIT", "ILL", "TRAP", "IOT", "EMT",
|
||||
"FPE", "KILL", "BUS", "SEGV", "SYS", "PIPE", "ALRM", "TERM",
|
||||
"URG", "STOP", "TSTP", "CONT", "CHLD", "TTIN", "TTOU", "IO",
|
||||
"XCPU", "XFSZ", "VTALRM","PROF", "WINCH", "PWR", "USR1", "USR2", NULL
|
||||
};
|
||||
"zero", "HUP", "INT", "QUIT", "ILL", "TRAP", "IOT", "EMT", "FPE",
|
||||
"KILL", "BUS", "SEGV", "SYS", "PIPE", "ALRM", "TERM", "URG", "STOP",
|
||||
"TSTP", "CONT", "CHLD", "TTIN", "TTOU", "IO", "XCPU", "XFSZ", "VTALRM",
|
||||
"PROF", "WINCH", "PWR", "USR1", "USR2", NULL};
|
||||
#endif
|
||||
|
||||
void *
|
||||
xmalloc(size_t size) {
|
||||
if (size == 0)
|
||||
return NULL;
|
||||
void *p = malloc(size);
|
||||
if (!p)
|
||||
abort();
|
||||
return p;
|
||||
void *xmalloc(size_t size) {
|
||||
if (size == 0) return NULL;
|
||||
void *p = malloc(size);
|
||||
if (!p) abort();
|
||||
return p;
|
||||
}
|
||||
|
||||
void *
|
||||
xrealloc(void *p, size_t size) {
|
||||
if ((size == 0) && (p == NULL))
|
||||
return NULL;
|
||||
p = realloc(p, size);
|
||||
if (!p)
|
||||
abort();
|
||||
return p;
|
||||
void *xrealloc(void *p, size_t size) {
|
||||
if ((size == 0) && (p == NULL)) return NULL;
|
||||
p = realloc(p, size);
|
||||
if (!p) abort();
|
||||
return p;
|
||||
}
|
||||
|
||||
char *
|
||||
uppercase(char *str) {
|
||||
int i = 0;
|
||||
do {
|
||||
str[i] = (char) toupper(str[i]);
|
||||
} while (str[i++] != '\0');
|
||||
return str;
|
||||
char *uppercase(char *str) {
|
||||
int i = 0;
|
||||
do {
|
||||
str[i] = (char)toupper(str[i]);
|
||||
} while (str[i++] != '\0');
|
||||
return str;
|
||||
}
|
||||
|
||||
bool
|
||||
endswith(const char *str, const char *suffix) {
|
||||
size_t str_len = strlen(str);
|
||||
size_t suffix_len = strlen(suffix);
|
||||
return str_len > suffix_len && !strcmp(str + (str_len - suffix_len), suffix);
|
||||
bool endswith(const char *str, const char *suffix) {
|
||||
size_t str_len = strlen(str);
|
||||
size_t suffix_len = strlen(suffix);
|
||||
return str_len > suffix_len && !strcmp(str + (str_len - suffix_len), suffix);
|
||||
}
|
||||
|
||||
int
|
||||
get_sig_name(int sig, char *buf, size_t len) {
|
||||
int n = snprintf(buf, len, "SIG%s", sig < NSIG ? sys_signame[sig] : "unknown");
|
||||
uppercase(buf);
|
||||
return n;
|
||||
int get_sig_name(int sig, char *buf, size_t len) {
|
||||
int n =
|
||||
snprintf(buf, len, "SIG%s", sig < NSIG ? sys_signame[sig] : "unknown");
|
||||
uppercase(buf);
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
get_sig(const char *sig_name) {
|
||||
for (int sig = 1; sig < NSIG; sig++) {
|
||||
const char *name = sys_signame[sig];
|
||||
if (name != NULL && (strcasecmp(name, sig_name) == 0 || strcasecmp(name, sig_name + 3) == 0))
|
||||
return sig;
|
||||
}
|
||||
return atoi(sig_name);
|
||||
int get_sig(const char *sig_name) {
|
||||
for (int sig = 1; sig < NSIG; sig++) {
|
||||
const char *name = sys_signame[sig];
|
||||
if (name != NULL && (strcasecmp(name, sig_name) == 0 ||
|
||||
strcasecmp(name, sig_name + 3) == 0))
|
||||
return sig;
|
||||
}
|
||||
return atoi(sig_name);
|
||||
}
|
||||
|
||||
bool
|
||||
fd_set_cloexec(const int fd) {
|
||||
int flags = fcntl(fd, F_GETFD);
|
||||
if (flags < 0)
|
||||
return false;
|
||||
return (flags & FD_CLOEXEC) == 0 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != -1;
|
||||
bool fd_set_cloexec(const int fd) {
|
||||
int flags = fcntl(fd, F_GETFD);
|
||||
if (flags < 0) return false;
|
||||
return (flags & FD_CLOEXEC) == 0 ||
|
||||
fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != -1;
|
||||
}
|
||||
|
||||
int
|
||||
open_uri(char *uri) {
|
||||
int open_uri(char *uri) {
|
||||
#ifdef __APPLE__
|
||||
char command[256];
|
||||
sprintf(command, "open %s > /dev/null 2>&1", uri);
|
||||
return system(command);
|
||||
char command[256];
|
||||
sprintf(command, "open %s > /dev/null 2>&1", uri);
|
||||
return system(command);
|
||||
#elif defined(_WIN32) || defined(__CYGWIN__)
|
||||
return ShellExecute(0, 0, uri, 0, 0 , SW_SHOW) > 32 ? 0 : 1;
|
||||
return ShellExecute(0, 0, uri, 0, 0, SW_SHOW) > 32 ? 0 : 1;
|
||||
#else
|
||||
// check if X server is running
|
||||
if (system("xset -q > /dev/null 2>&1"))
|
||||
return 1;
|
||||
char command[256];
|
||||
sprintf(command, "xdg-open %s > /dev/null 2>&1", uri);
|
||||
return system(command);
|
||||
// check if X server is running
|
||||
if (system("xset -q > /dev/null 2>&1")) return 1;
|
||||
char command[256];
|
||||
sprintf(command, "xdg-open %s > /dev/null 2>&1", uri);
|
||||
return system(command);
|
||||
#endif
|
||||
}
|
||||
|
||||
// https://github.com/darkk/redsocks/blob/master/base64.c
|
||||
char *
|
||||
base64_encode(const unsigned char *buffer, size_t length) {
|
||||
static const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
char *ret, *dst;
|
||||
unsigned i_bits = 0;
|
||||
int i_shift = 0;
|
||||
int bytes_remaining = (int) length;
|
||||
char *base64_encode(const unsigned char *buffer, size_t length) {
|
||||
static const char b64[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
char *ret, *dst;
|
||||
unsigned i_bits = 0;
|
||||
int i_shift = 0;
|
||||
int bytes_remaining = (int)length;
|
||||
|
||||
ret = dst = xmalloc((size_t) (((length + 2) / 3 * 4) + 1));
|
||||
while (bytes_remaining) {
|
||||
i_bits = (i_bits << 8) + *buffer++;
|
||||
bytes_remaining--;
|
||||
i_shift += 8;
|
||||
ret = dst = xmalloc((size_t)(((length + 2) / 3 * 4) + 1));
|
||||
while (bytes_remaining) {
|
||||
i_bits = (i_bits << 8) + *buffer++;
|
||||
bytes_remaining--;
|
||||
i_shift += 8;
|
||||
|
||||
do {
|
||||
*dst++ = b64[(i_bits << 6 >> i_shift) & 0x3f];
|
||||
i_shift -= 6;
|
||||
} while (i_shift > 6 || (bytes_remaining == 0 && i_shift > 0));
|
||||
}
|
||||
while ((dst - ret) & 3)
|
||||
*dst++ = '=';
|
||||
*dst = '\0';
|
||||
do {
|
||||
*dst++ = b64[(i_bits << 6 >> i_shift) & 0x3f];
|
||||
i_shift -= 6;
|
||||
} while (i_shift > 6 || (bytes_remaining == 0 && i_shift > 0));
|
||||
}
|
||||
while ((dst - ret) & 3) *dst++ = '=';
|
||||
*dst = '\0';
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
29
src/utils.h
29
src/utils.h
@@ -2,39 +2,30 @@
|
||||
#define TTYD_UTIL_H
|
||||
|
||||
// malloc with NULL check
|
||||
void *
|
||||
xmalloc(size_t size);
|
||||
void *xmalloc(size_t size);
|
||||
|
||||
// realloc with NULL check
|
||||
void *
|
||||
xrealloc(void *p, size_t size);
|
||||
void *xrealloc(void *p, size_t size);
|
||||
|
||||
// Convert a string to upper case
|
||||
char *
|
||||
uppercase(char *str);
|
||||
char *uppercase(char *str);
|
||||
|
||||
// Check whether str ends with suffix
|
||||
bool
|
||||
endswith(const char *str, const char *suffix);
|
||||
bool endswith(const char *str, const char *suffix);
|
||||
|
||||
// Get human readable signal string
|
||||
int
|
||||
get_sig_name(int sig, char *buf, size_t len);
|
||||
int get_sig_name(int sig, char *buf, size_t len);
|
||||
|
||||
// Get signal code from string like SIGHUP
|
||||
int
|
||||
get_sig(const char *sig_name);
|
||||
int get_sig(const char *sig_name);
|
||||
|
||||
// Set the given file descriptor close-on-exec
|
||||
bool
|
||||
fd_set_cloexec(const int fd);
|
||||
bool fd_set_cloexec(const int fd);
|
||||
|
||||
// Open uri with the default application of system
|
||||
int
|
||||
open_uri(char *uri);
|
||||
int open_uri(char *uri);
|
||||
|
||||
// Encode text to base64, the caller should free the returned string
|
||||
char *
|
||||
base64_encode(const unsigned char *buffer, size_t length);
|
||||
char *base64_encode(const unsigned char *buffer, size_t length);
|
||||
|
||||
#endif //TTYD_UTIL_H
|
||||
#endif // TTYD_UTIL_H
|
||||
|
||||
Reference in New Issue
Block a user