From 9b0a937e359a255ed5cad4246ec707cf25a2c5ef Mon Sep 17 00:00:00 2001 From: "zodiac@gmail.com" Date: Mon, 29 Mar 2010 16:40:17 +0000 Subject: [PATCH] Started working on support for WebSockets. Fixed some compiler warnings when compiling with -Wextra Thanks to Jan Jaeger's excellent bug report, made some changes that should make it easier to build ShellInABox for OpenWRT. git-svn-id: https://shellinabox.googlecode.com/svn/trunk@202 0da03de8-d603-11dd-86c2-0f8696b7b6f9 --- COPYING | 2 +- ChangeLog | 9 + Makefile.am | 38 +-- Makefile.in | 38 +-- config.h | 5 +- config.h.in | 3 + configure | 21 +- configure.ac | 8 +- debian/copyright | 2 +- demo/vt100.js | 6 +- libhttp/http.h | 15 +- libhttp/httpconnection.c | 407 ++++++++++++++++++++++++++++---- libhttp/httpconnection.h | 19 +- libhttp/libhttp.sym | 3 + libhttp/server.c | 22 +- libhttp/server.h | 5 +- libhttp/ssl.c | 15 +- libhttp/url.c | 3 +- shellinabox/launcher.c | 29 ++- shellinabox/privileges.c | 8 +- shellinabox/service.c | 8 +- shellinabox/service.h | 6 +- shellinabox/session.c | 7 +- shellinabox/shell_in_a_box.js | 6 +- shellinabox/shell_in_a_box.jspp | 4 +- shellinabox/shellinaboxd.c | 13 +- shellinabox/usercss.c | 7 +- shellinabox/vt100.js | 6 +- shellinabox/vt100.jspp | 4 +- 29 files changed, 581 insertions(+), 138 deletions(-) diff --git a/COPYING b/COPYING index 2992243..ad46cef 100644 --- a/COPYING +++ b/COPYING @@ -1,4 +1,4 @@ -Copyright (C) 2008-2009 Markus Gutschke +Copyright (C) 2008-2010 Markus Gutschke This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as diff --git a/ChangeLog b/ChangeLog index 2a519ac..339feb0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2010-03-29 Markus Gutschke + + * Started working on support for WebSockets. + + * Fixed some compiler warnings when compiling with -Wextra + + * Thanks to Jan Jaeger's excellent bug report, made some changes + that should make it easier to build ShellInABox for OpenWRT. + 2009-12-10 Markus Gutschke * Add .note.GNU-stack to all object files so that the generated diff --git a/Makefile.am b/Makefile.am index f0ab2f6..ef5a89c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,6 +2,8 @@ AM_CPPFLAGS = AM_CFLAGS = -g -std=gnu99 -Wall AM_LDFLAGS = -g +OBJCOPY ?= objcopy + noinst_LTLIBRARIES = libhttp.la \ liblogging.la noinst_DATA = $(top_srcdir)/demo/demo.js @@ -234,37 +236,37 @@ clean-local: -rm -rf GNU-stack .css.o: - @echo objcopy "$<" "$@" - @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ + @echo $(OBJCOPY) "$<" "$@" + @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\ "$<" "$@" @-printf '\000' >GNU-stack && \ - objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ + $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \ rm -f GNU-stack .gif.o: - @echo objcopy "$<" "$@" - @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ + @echo $(OBJCOPY) "$<" "$@" + @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\ "$<" "$@" @-printf '\000' >GNU-stack && \ - objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ + $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \ rm -f GNU-stack .html.o: - @echo objcopy "$<" "$@" - @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ + @echo $(OBJCOPY) "$<" "$@" + @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\ "$<" "$@" @-printf '\000' >GNU-stack && \ - objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ + $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \ rm -f GNU-stack .ico.o: - @echo objcopy "$<" "$@" - @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ + @echo $(OBJCOPY) "$<" "$@" + @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\ "$<" "$@" @-printf '\000' >GNU-stack && \ - objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ + $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \ rm -f GNU-stack @@ -280,20 +282,20 @@ shellinabox/shell_in_a_box.o: shellinabox/shell_in_a_box.js config.h "$<" >"$@" .js.o: - @echo objcopy "$<" "$@" - @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ + @echo $(OBJCOPY) "$<" "$@" + @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\ "$<" "$@" @-printf '\000' >GNU-stack && \ - objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ + $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \ rm -f GNU-stack .wav.o: - @echo objcopy "$<" "$@" - @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ + @echo $(OBJCOPY) "$<" "$@" + @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\ "$<" "$@" @-printf '\000' >GNU-stack && \ - objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ + $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \ rm -f GNU-stack diff --git a/Makefile.in b/Makefile.in index 75627b3..5620d3e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1152,6 +1152,8 @@ uninstall-man: uninstall-man1 uninstall-man uninstall-man1 +OBJCOPY ?= objcopy + libtool: $(LIBTOOL_DEPS) $(SHELL) ./config.status --recheck @@ -1247,35 +1249,35 @@ clean-local: -rm -rf GNU-stack .css.o: - @echo objcopy "$<" "$@" - @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ + @echo $(OBJCOPY) "$<" "$@" + @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\ "$<" "$@" @-printf '\000' >GNU-stack && \ - objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ + $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \ rm -f GNU-stack .gif.o: - @echo objcopy "$<" "$@" - @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ + @echo $(OBJCOPY) "$<" "$@" + @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\ "$<" "$@" @-printf '\000' >GNU-stack && \ - objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ + $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \ rm -f GNU-stack .html.o: - @echo objcopy "$<" "$@" - @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ + @echo $(OBJCOPY) "$<" "$@" + @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\ "$<" "$@" @-printf '\000' >GNU-stack && \ - objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ + $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \ rm -f GNU-stack .ico.o: - @echo objcopy "$<" "$@" - @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ + @echo $(OBJCOPY) "$<" "$@" + @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\ "$<" "$@" @-printf '\000' >GNU-stack && \ - objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ + $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \ rm -f GNU-stack shellinabox/shell_in_a_box.o: shellinabox/shell_in_a_box.js config.h @@ -1290,19 +1292,19 @@ shellinabox/shell_in_a_box.o: shellinabox/shell_in_a_box.js config.h "$<" >"$@" .js.o: - @echo objcopy "$<" "$@" - @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ + @echo $(OBJCOPY) "$<" "$@" + @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\ "$<" "$@" @-printf '\000' >GNU-stack && \ - objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ + $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \ rm -f GNU-stack .wav.o: - @echo objcopy "$<" "$@" - @objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ + @echo $(OBJCOPY) "$<" "$@" + @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\ "$<" "$@" @-printf '\000' >GNU-stack && \ - objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ + $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \ rm -f GNU-stack # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/config.h b/config.h index 15dd68f..0b51859 100644 --- a/config.h +++ b/config.h @@ -25,6 +25,9 @@ /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 +/* Define to 1 if you have support for isnan */ +#define HAVE_ISNAN 1 + /* Define to 1 if you have the header file. */ /* #undef HAVE_LIBUTIL_H */ @@ -141,7 +144,7 @@ #define STDC_HEADERS 1 /* Most recent revision number in the version control system */ -#define VCS_REVISION "200" +#define VCS_REVISION "202" /* Version number of package */ #define VERSION "2.10" diff --git a/config.h.in b/config.h.in index 692ce14..000e338 100644 --- a/config.h.in +++ b/config.h.in @@ -24,6 +24,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H +/* Define to 1 if you have support for isnan */ +#undef HAVE_ISNAN + /* Define to 1 if you have the header file. */ #undef HAVE_LIBUTIL_H diff --git a/configure b/configure index 1626dd1..b617819 100755 --- a/configure +++ b/configure @@ -2325,7 +2325,7 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $ ac_compiler_gnu=$ac_cv_c_compiler_gnu -VCS_REVISION=200 +VCS_REVISION=202 cat >>confdefs.h <<_ACEOF @@ -10682,6 +10682,25 @@ if ac_fn_c_try_link "$LINENO"; then : $as_echo "#define HAVE_SIGWAIT 1" >>confdefs.h +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +isnan(0.0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + +$as_echo "#define HAVE_ISNAN 1" >>confdefs.h + fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext diff --git a/configure.ac b/configure.ac index a42b0e5..70ffbc4 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ AC_PREREQ(2.57) dnl This is the one location where the authoritative version number is stored AC_INIT(shellinabox, 2.10, markus@shellinabox.com) -VCS_REVISION=200 +VCS_REVISION=202 AC_SUBST(VCS_REVISION) AC_DEFINE_UNQUOTED(VCS_REVISION, "${VCS_REVISION}", [Most recent revision number in the version control system]) @@ -55,6 +55,12 @@ AC_TRY_LINK([#include [AC_DEFINE(HAVE_SIGWAIT, 1, Define to 1 if you have a working sigwait)]) +dnl Not every system has support for isnan() +AC_TRY_LINK([#include ], + [isnan(0.0);], + [AC_DEFINE(HAVE_ISNAN, 1, + Define to 1 if you have support for isnan)]) + dnl On some systems, calling /bin/login does not work. Disable the LOGIN dnl feature, if the user tells us that it does not do the right thing. AC_ARG_ENABLE(login, diff --git a/debian/copyright b/debian/copyright index f86ac29..cf4aaf7 100644 --- a/debian/copyright +++ b/debian/copyright @@ -5,7 +5,7 @@ It was downloaded from http://shellinabox.com/ Upstream Author: markus@shellinabox.com -Copyright (c) 2008-2009, Markus Gutschke +Copyright (c) 2008-2010, Markus Gutschke All rights reserved. This program is free software; you can redistribute it and/or modify diff --git a/demo/vt100.js b/demo/vt100.js index c46e0c0..e6fbf79 100644 --- a/demo/vt100.js +++ b/demo/vt100.js @@ -1,5 +1,5 @@ // VT100.js -- JavaScript based terminal emulator -// Copyright (C) 2008-2009 Markus Gutschke +// Copyright (C) 2008-2010 Markus Gutschke // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -1955,8 +1955,8 @@ VT100.prototype.toggleBell = function() { }; VT100.prototype.about = function() { - alert("VT100 Terminal Emulator " + "2.10 (revision 200)" + - "\nCopyright 2008-2009 by Markus Gutschke\n" + + alert("VT100 Terminal Emulator " + "2.10 (revision 202)" + + "\nCopyright 2008-2010 by Markus Gutschke\n" + "For more information check http://shellinabox.com"); }; diff --git a/libhttp/http.h b/libhttp/http.h index e7840fa..cec2fe0 100644 --- a/libhttp/http.h +++ b/libhttp/http.h @@ -1,5 +1,5 @@ // http.h -- Library for implementing embedded custom HTTP servers -// Copyright (C) 2008-2009 Markus Gutschke +// Copyright (C) 2008-2010 Markus Gutschke // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -56,7 +56,13 @@ #define HTTP_SUSPEND 3 #define HTTP_PARTIAL_REPLY 4 +#define WS_START_OF_FRAME 0x0100 +#define WS_END_OF_FRAME 0x0200 +#define WS_CONNECTION_OPENED 0xFF00 +#define WS_CONNECTION_CLOSED 0x7F00 + #define NO_MSG "\001" +#define BINARY_MSG "\001%d%p" #define NOINTR(x) ({ int i__; while ((i__ = (x)) < 0 && errno == EINTR); i__;}) @@ -77,6 +83,9 @@ void serverRegisterHttpHandler(Server *server, const char *url, void serverRegisterStreamingHttpHandler(Server *server, const char *url, int (*handler)(HttpConnection *, void *), void *arg); +void serverRegisterWebSocketHandler(Server *server, const char *url, + int (*handler)(HttpConnection *, void *, int, const char *, int), + void *arg); ServerConnection *serverAddConnection(Server *server, int fd, int (*handleConnection)(ServerConnection *, void *arg, short *events, @@ -109,6 +118,10 @@ void *httpSetPrivate(HttpConnection *http, void *private); void httpSendReply(HttpConnection *http, int code, const char *msg, const char *fmt, ...) __attribute__((format(printf, 4, 5))); +void httpSendWebSocketTextMsg(HttpConnection *http, int type, const char *fmt, + ...) __attribute__((format(printf, 3, 4))); +void httpSendWebSocketBinaryMsg(HttpConnection *http, int type, + const void *buf, int len); void httpExitLoop(HttpConnection *http, int exitAll); Server *httpGetServer(const HttpConnection *http); ServerConnection *httpGetServerConnection(const HttpConnection *); diff --git a/libhttp/httpconnection.c b/libhttp/httpconnection.c index c8e69f6..ad391fa 100644 --- a/libhttp/httpconnection.c +++ b/libhttp/httpconnection.c @@ -1,5 +1,5 @@ // httpconnection.c -- Manage state machine for HTTP connections -// Copyright (C) 2008-2009 Markus Gutschke +// Copyright (C) 2008-2010 Markus Gutschke // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -66,6 +66,9 @@ #ifdef HAVE_STRLCAT #define strncat(a,b,c) ({ char *_a = (a); strlcat(_a, (b), (c)+1); _a; }) #endif +#ifndef HAVE_ISNAN +#define isnan(x) ({ typeof(x) _x = (x); _x != _x; }) +#endif #define max(a, b) ({ typeof(a) _a = (a); typeof(b) _b = (b); \ _a > _b ? _a : _b; }) @@ -223,8 +226,9 @@ static char *strcasestr(const char *haystack, const char *needle) { static int httpFinishCommand(struct HttpConnection *http) { int rc = HTTP_DONE; - if (http->callback && !http->done) { - rc = http->callback(http, http->arg, NULL, 0); + if ((http->callback || http->websocketHandler) && !http->done) { + rc = http->callback ? http->callback(http, http->arg, NULL, 0) + : http->websocketHandler(http, http->arg, WS_CONNECTION_CLOSED, NULL,0); check(rc != HTTP_SUSPEND); check(rc != HTTP_PARTIAL_REPLY); http->callback = NULL; @@ -267,6 +271,7 @@ static int httpFinishCommand(struct HttpConnection *http) { } static void httpDestroyHeaders(void *arg, char *key, char *value) { + (void)arg; free(key); free(value); } @@ -296,7 +301,7 @@ static char *getPeerName(int fd, int *port, int numericHosts) { } static void httpSetState(struct HttpConnection *http, int state) { - if (state == http->state) { + if (state == (int)http->state) { return; } @@ -372,7 +377,9 @@ void initHttpConnection(struct HttpConnection *http, struct Server *server, http->msgOffset = 0; http->totalWritten = 0; http->expecting = 0; + http->websocketType = WS_UNDEFINED; http->callback = NULL; + http->websocketHandler = NULL; http->arg = NULL; http->private = NULL; http->code = 200; @@ -388,8 +395,12 @@ void initHttpConnection(struct HttpConnection *http, struct Server *server, void destroyHttpConnection(struct HttpConnection *http) { if (http) { if (http->isSuspended || http->isPartialReply) { - if (http->callback && !http->done) { - http->callback(http, http->arg, NULL, 0); + if (!http->done) { + if (http->callback) { + http->callback(http, http->arg, NULL, 0); + } else if (http->websocketHandler) { + http->websocketHandler(http, http->arg, WS_CONNECTION_CLOSED,NULL,0); + } } http->callback = NULL; http->isSuspended = 0; @@ -774,41 +785,41 @@ void httpTransferPartialReply(struct HttpConnection *http, char *msg, int len){ static int httpHandleCommand(struct HttpConnection *http, const struct Trie *handlers) { debug("Handling \"%s\" \"%s\"", http->method, http->path); - const char *contentLength = getFromHashMap(&http->header, - "content-length"); + const char *contentLength = getFromHashMap(&http->header, + "content-length"); if (contentLength != NULL && *contentLength) { char *endptr; - http->expecting = strtol(contentLength, - &endptr, 10); + http->expecting = strtol(contentLength, + &endptr, 10); if (*endptr) { // Invalid length. Read until end of stream and then close // connection. - http->expecting = -1; + http->expecting = -1; } } else { // Unknown length. Read until end of stream and then close // connection. - http->expecting = -1; + http->expecting = -1; } if (!strcmp(http->method, "OPTIONS")) { - char *response = stringPrintf(NULL, + char *response = stringPrintf(NULL, "HTTP/1.1 200 OK\r\n" "Content-Length: 0\r\n" "Allow: GET, POST, OPTIONS\r\n" "\r\n"); httpTransfer(http, response, strlen(response)); if (http->expecting < 0) { - http->expecting = 0; + http->expecting = 0; } return HTTP_READ_MORE; } else if (!strcmp(http->method, "GET")) { if (http->expecting < 0) { - http->expecting = 0; + http->expecting = 0; } } else if (!strcmp(http->method, "POST")) { } else if (!strcmp(http->method, "HEAD")) { if (http->expecting < 0) { - http->expecting = 0; + http->expecting = 0; } } else if (!strcmp(http->method, "PUT") || !strcmp(http->method, "DELETE") || @@ -820,52 +831,116 @@ static int httpHandleCommand(struct HttpConnection *http, httpSendReply(http, 501, "Method Not Implemented", NO_MSG); return HTTP_DONE; } - const char *host = getFromHashMap(&http->header, - "host"); + const char *host = getFromHashMap(&http->header, + "host"); if (host) { - for (char ch; (ch = *host) != '\000'; host++) { + for (char ch, *ptr = (char *)host; (ch = *ptr) != '\000'; ptr++) { if (ch == ':') { - *(char *)host = '\000'; + *ptr = '\000'; break; } if (ch != '-' && ch != '.' && (ch < '0' ||(ch > '9' && ch < 'A') || - (ch > 'Z' && ch < 'a')|| ch > 'z')) { + (ch > 'Z' && ch < 'a')||(ch > 'z' && ch <= 0x7E))) { httpSendReply(http, 400, "Bad Request", NO_MSG); return HTTP_DONE; } } } + char *diff; struct HttpHandler *h = (struct HttpHandler *)getFromTrie(handlers, http->path, &diff); + if (h) { - check(diff); - while (diff > http->path && diff[-1] == '/') { - diff--; + if (h->websocketHandler) { + // Check for WebSocket handshake + const char *upgrade = getFromHashMap(&http->header, + "upgrade"); + if (upgrade && !strcmp(upgrade, "WebSocket")) { + const char *connection = getFromHashMap(&http->header, + "connection"); + if (connection && !strcmp(connection, "Upgrade")) { + const char *origin = getFromHashMap(&http->header, + "origin"); + if (origin) { + for (const char *ptr = origin; *ptr; ptr++) { + if ((unsigned char)*ptr < ' ') { + goto bad_ws_upgrade; + } + } + + const char *protocol = getFromHashMap(&http->header, + "websocket-protocol"); + if (protocol) { + for (const char *ptr = protocol; *ptr; ptr++) { + if ((unsigned char)*ptr < ' ') { + goto bad_ws_upgrade; + } + } + } + char *port = NULL; + if (http->port != (http->sslHndl ? 443 : 80)) { + port = stringPrintf(NULL, + ":%d", http->port); + } + char *response = stringPrintf(NULL, + "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" + "Upgrade: WebSocket\r\n" + "Connection: Upgrade\r\n" + "WebSocket-Origin: %s\r\n" + "WebSocket-Location: %s://%s%s%s\r\n" + "%s%s%s" + "\r\n", + origin, + http->sslHndl ? "wss" : "ws", host && *host ? host : "localhost", + port ? port : "", http->path, + protocol ? "WebSocket-Protocol: " : "", + protocol ? protocol : "", + protocol ? "\r\n" : ""); + free(port); + debug("Switching to WebSockets"); + httpTransfer(http, response, strlen(response)); + if (http->expecting < 0) { + http->expecting = 0; + } + http->websocketHandler = h->websocketHandler; + httpSetState(http, WEBSOCKET); + return HTTP_READ_MORE; + } + } + } } - if (!*diff || *diff == '/' || *diff == '?' || *diff == '#') { - check(!http->matchedPath); - check(!http->pathInfo); - check(!http->query); + bad_ws_upgrade:; - check(http->matchedPath = malloc(diff - http->path + 1)); - memcpy(http->matchedPath, http->path, diff - http->path); - http->matchedPath[diff - http->path] = '\000'; - - const char *query = strchr(diff, '?'); - if (*diff && *diff != '?') { - const char *endOfInfo = query - ? query : strrchr(diff, '\000'); - check(http->pathInfo = malloc(endOfInfo - diff + 1)); - memcpy(http->pathInfo, diff, endOfInfo - diff); - http->pathInfo[endOfInfo - diff] = '\000'; + if (h->handler) { + check(diff); + while (diff > http->path && diff[-1] == '/') { + diff--; } + if (!*diff || *diff == '/' || *diff == '?' || *diff == '#') { + check(!http->matchedPath); + check(!http->pathInfo); + check(!http->query); - if (query) { - check(http->query = strdup(query + 1)); + check(http->matchedPath = malloc(diff - http->path + 1)); + memcpy(http->matchedPath, http->path, diff - http->path); + http->matchedPath[diff - http->path] = '\000'; + + const char *query = strchr(diff, '?'); + if (*diff && *diff != '?') { + const char *endOfInfo = query + ? query : strrchr(diff, '\000'); + check(http->pathInfo = malloc(endOfInfo - diff + 1)); + memcpy(http->pathInfo, diff, endOfInfo - diff); + http->pathInfo[endOfInfo - diff] = '\000'; + } + + if (query) { + check(http->query = strdup(query + 1)); + } + return h->handler(http, h->arg); } - return h->handler(http, h->arg); } } httpSendReply(http, 404, "File Not Found", NO_MSG); @@ -1011,13 +1086,20 @@ static int httpParseHeaders(struct HttpConnection *http, serverSetTimeout(connection, CONNECTION_TIMEOUT); } check(!http->done); - if (!http->expecting && http->callback) { - rc = http->callback(http, http->arg, "", 0); - if (rc != HTTP_READ_MORE) { - goto retry; + if (!http->expecting) { + if (http->callback) { + rc = http->callback(http, http->arg, "", 0); + if (rc != HTTP_READ_MORE) { + goto retry; + } + } else if (http->websocketHandler) { + http->websocketHandler(http, http->arg, WS_CONNECTION_OPENED, + NULL, 0); } } - httpSetState(http, http->expecting ? PAYLOAD : COMMAND); + if (http->state != WEBSOCKET) { + httpSetState(http, http->expecting ? PAYLOAD : COMMAND); + } break; case HTTP_SUSPEND: http->isSuspended = 1; @@ -1187,6 +1269,140 @@ static int httpParsePayload(struct HttpConnection *http, int offset, return consumed; } +static int httpHandleWebSocket(struct HttpConnection *http, int offset, + const char *buf, int bytes) { + check(http->websocketHandler); + int ch = 0x00; + while (bytes > offset) { + if (http->websocketType & WS_UNDEFINED) { + ch = httpGetChar(http, buf, bytes, &offset); + check(ch >= 0); + if (http->websocketType & 0xFF) { + // Reading another byte of length information. + if (http->expecting > 0xFFFFFF) { + return 0; + } + http->expecting = (128 * http->expecting) + (ch & 0x7F); + if ((ch & 0x80) == 0) { + // Done reading length information. + http->websocketType &= ~WS_UNDEFINED; + + // ch is used to detect when we read the terminating byte in text + // mode. In binary mode, it must be set to something other than 0xFF. + ch = 0x00; + } + } else { + // Reading first byte of frame. + http->websocketType = (ch & 0xFF) | WS_START_OF_FRAME; + if (ch & 0x80) { + // For binary data, we have to read the length before we can start + // processing payload. + http->websocketType |= WS_UNDEFINED; + http->expecting = 0; + } + } + } else if (http->websocketType & 0x80) { + // Binary data + if (http->expecting) { + if (offset < 0) { + handle_partial: + check(-offset <= http->partialLength); + int len = -offset; + if (len >= http->expecting) { + len = http->expecting; + http->websocketType |= WS_END_OF_FRAME; + } + if (len && + http->websocketHandler(http, http->arg, http->websocketType, + http->partial + http->partialLength + offset, + len) != HTTP_DONE) { + return 0; + } + + if (ch == 0xFF) { + // In text mode, we jump to handle_partial, when we find the + // terminating 0xFF byte. If so, we should try to consume it now. + if (len < http->partialLength) { + len++; + http->websocketType = WS_UNDEFINED; + } + } + + if (len == http->partialLength) { + free(http->partial); + http->partial = NULL; + http->partialLength = 0; + } else { + memmove(http->partial, http->partial + len, + http->partialLength - len); + http->partialLength -= len; + } + offset += len; + http->expecting -= len; + } else { + handle_buffered:; + int len = bytes - offset; + if (len >= http->expecting) { + len = http->expecting; + http->websocketType |= WS_END_OF_FRAME; + } + if (len && + http->websocketHandler(http, http->arg, http->websocketType, + buf + offset, len) != HTTP_DONE) { + return 0; + } + + if (ch == 0xFF) { + // In text mode, we jump to handle_buffered, when we find the + // terminating 0xFF byte. If so, we should consume it now. + check(offset + len < bytes); + len++; + http->websocketType = WS_UNDEFINED; + } + offset += len; + http->expecting -= len; + } + http->websocketType &= ~(WS_START_OF_FRAME | WS_END_OF_FRAME); + } else { + // Read all data. Go back to looking for a new frame header. + http->websocketType = WS_UNDEFINED; + } + } else { + // Process text data until we find a 0xFF bytes. + int i = offset; + + // If we have partial data, process that first. + while (i < 0) { + ch = httpGetChar(http, buf, bytes, &i); + check(ch != -1); + + // Terminate when we either find the 0xFF, or we have reached the end + // of partial data. + if (ch == 0xFF || !i) { + // Set WS_END_OF_FRAME, iff we have found the 0xFF marker. + http->expecting = i - offset - (ch == 0xFF); + goto handle_partial; + } + } + + // Read all remaining buffered bytes (i.e. positive offset). + while (bytes > i) { + ch = httpGetChar(http, buf, bytes, &i); + check(ch != -1); + + // Terminate when we either find the 0xFF, or we have reached the end + // of buffered data. + if (ch == 0xFF || bytes == i) { + // Set WS_END_OF_FRAME, iff we have found the 0xFF marker. + http->expecting = i - offset - (ch == 0xFF); + goto handle_buffered; + } + } + } + } + return 1; +} + int httpHandleConnection(struct ServerConnection *connection, void *http_, short *events, short revents) { struct HttpConnection *http = (struct HttpConnection *)http_; @@ -1219,6 +1435,7 @@ int httpHandleConnection(struct ServerConnection *connection, void *http_, bytes = 0; } } + if (bytes > 0 && http->state == SNIFFING_SSL) { // Assume that all legitimate HTTP commands start with a sequence of // letters followed by a space character. If we don't see this pattern, @@ -1241,7 +1458,7 @@ int httpHandleConnection(struct ServerConnection *connection, void *http_, strcmp(method, "CONNECT"); http->state = COMMAND; break; - } else if (j >= sizeof(method)-1 || + } else if (j >= (int)sizeof(method)-1 || ch < 'A' || (ch > 'Z' && ch < 'a') || ch > 'z') { isSSL = 1; http->state = COMMAND; @@ -1262,6 +1479,7 @@ int httpHandleConnection(struct ServerConnection *connection, void *http_, } } } + if (bytes > 0 || (eof && http->partial)) { check(!!http->partial == !!http->partialLength); int offset = -http->partialLength; @@ -1331,7 +1549,16 @@ int httpHandleConnection(struct ServerConnection *connection, void *http_, consumed = len; pushBack = bytes - offset - len; } + } else if (http->state == WEBSOCKET) { + if (!httpHandleWebSocket(http, offset, buf, bytes)) { + httpCloseRead(http); + break; + } + consumed += bytes - offset; + } else { + check(0); } + if (pushBack) { check(offset + pushBack == bytes); if (offset >= 0) { @@ -1383,6 +1610,7 @@ int httpHandleConnection(struct ServerConnection *connection, void *http_, break; case PAYLOAD: case DISCARD_PAYLOAD: + case WEBSOCKET: http->expecting = 0; httpCloseRead(http); httpSetState(http, COMMAND); @@ -1563,6 +1791,89 @@ void httpSendReply(struct HttpConnection *http, int code, } } +void httpSendWebSocketTextMsg(struct HttpConnection *http, int type, + const char *fmt, ...) { + check(type >= 0 && type <= 0x7F); + va_list ap; + va_start(ap, fmt); + char *buf; + int len; + if (strcmp(fmt, BINARY_MSG)) { + // Send a printf() style text message + buf = vStringPrintf(NULL, fmt, ap); + len = strlen(buf); + } else { + // Send a binary message + len = va_arg(ap, int); + buf = va_arg(ap, char *); + } + va_end(ap); + check(len >= 0 && len < 0x60000000); + + // We assume that all input data is directly mapped in the range 0..255 + // (e.g. ISO-8859-1). In order to transparently send it over a web socket, + // we have to encode it in UTF-8. + int utf8Len = len + 2; + for (int i = 0; i < len; ++i) { + if (buf[i] & 0x80) { + ++utf8Len; + } + } + char *utf8; + check(utf8 = malloc(utf8Len)); + utf8[0] = type; + for (int i = 0, j = 1; i < len; ++i) { + unsigned char ch = buf[i]; + if (ch & 0x80) { + utf8[j++] = 0xC0 + (ch >> 6); + utf8[j++] = 0x80 + (ch & 0x3F); + } else { + utf8[j++] = ch; + } + check(j < utf8Len); + } + utf8[utf8Len-1] = '\xFF'; + + // Free our temporary buffer, if we actually did allocate one. + if (strcmp(fmt, BINARY_MSG)) { + free(buf); + } + + // Send to browser. + httpTransfer(http, utf8, utf8Len); +} + +void httpSendWebSocketBinaryMsg(struct HttpConnection *http, int type, + const void *buf, int len) { + check(type >= 0x80 && type <= 0xFF); + check(len > 0 && len < 0x7FFFFFF0); + + // Allocate buffer for header and payload. + char *data; + check(data = malloc(len + 6)); + data[0] = type; + + // Convert length to base-128. + int i = 0; + int l = len; + do { + data[++i] = 0x80 + (l & 0x7F); + l /= 128; + } while (l); + data[i] &= 0x7F; + + // Reverse digits, so that they are big-endian. + for (int j = 0; j < i/2; ++j) { + char ch = data[1+j]; + data[1+j] = data[i-j]; + data[i-j] = ch; + } + + // Transmit header and payload. + memmove(data + i + 1, buf, len); + httpTransfer(http, data, len + i + 1); +} + void httpExitLoop(struct HttpConnection *http, int exitAll) { serverExitLoop(http->server, exitAll); } diff --git a/libhttp/httpconnection.h b/libhttp/httpconnection.h index 2c1e9cd..d0207c4 100644 --- a/libhttp/httpconnection.h +++ b/libhttp/httpconnection.h @@ -1,5 +1,5 @@ // httpconnection.h -- Manage state machine for HTTP connections -// Copyright (C) 2008-2009 Markus Gutschke +// Copyright (C) 2008-2010 Markus Gutschke // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -60,6 +60,10 @@ #define HTTP_SUSPEND 3 #define HTTP_PARTIAL_REPLY 4 +#define WS_UNDEFINED 0x1000 +#define WS_START_OF_FRAME 0x0100 +#define WS_END_OF_FRAME 0x0200 + #define NO_MSG "\001" struct HttpConnection { @@ -71,7 +75,8 @@ struct HttpConnection { int isSuspended; int isPartialReply; int done; - enum { SNIFFING_SSL, COMMAND, HEADERS, PAYLOAD, DISCARD_PAYLOAD } state; + enum { SNIFFING_SSL, COMMAND, HEADERS, PAYLOAD, DISCARD_PAYLOAD, + WEBSOCKET } state; char *peerName; int peerPort; char *url; @@ -91,8 +96,11 @@ struct HttpConnection { int msgOffset; int totalWritten; int expecting; + int websocketType; int (*callback)(struct HttpConnection *, void *, const char *,int); + int (*websocketHandler)(struct HttpConnection *, void *, + int, const char *, int); void *arg; void *private; int code; @@ -104,6 +112,8 @@ struct HttpConnection { struct HttpHandler { int (*handler)(struct HttpConnection *, void *); int (*streamingHandler)(struct HttpConnection *, void *, const char *, int); + int (*websocketHandler)(struct HttpConnection *, void *, int, + const char *, int); void *arg, *streamingArg; }; @@ -128,6 +138,11 @@ void *httpSetPrivate(struct HttpConnection *http, void *private); void httpSendReply(struct HttpConnection *http, int code, const char *msg, const char *fmt, ...) __attribute__((format(printf, 4, 5))); +void httpSendWebSocketTextMsg(struct HttpConnection *http, int type, + const char *fmt, ...) + __attribute__((format(printf, 3, 4))); +void httpSendWebSocketBinaryMsg(struct HttpConnection *http, int type, + const void *buf, int len); void httpExitLoop(struct HttpConnection *http, int exitAll); struct Server *httpGetServer(const struct HttpConnection *http); struct ServerConnection *httpGetServerConnection(const struct HttpConnection*); diff --git a/libhttp/libhttp.sym b/libhttp/libhttp.sym index 4eb0bbc..c8fed82 100644 --- a/libhttp/libhttp.sym +++ b/libhttp/libhttp.sym @@ -5,6 +5,7 @@ serverGetListeningPort serverGetFd serverRegisterHttpHandler serverRegisterStreamingHttpHandler +serverRegisterWebSocketHandler serverAddConnection serverDeleteConnection serverSetTimeout @@ -24,6 +25,8 @@ httpSetCallback httpGetPrivate httpSetPrivate httpSendReply +httpSendWebSocketTextMsg +httpSendWebSocketBinaryMsg httpExitLoop httpGetServer httpGetServerConnection diff --git a/libhttp/server.c b/libhttp/server.c index f52a269..41dfcb0 100644 --- a/libhttp/server.c +++ b/libhttp/server.c @@ -1,5 +1,5 @@ // server.c -- Generic server that can deal with HTTP connections -// Copyright (C) 2008-2009 Markus Gutschke +// Copyright (C) 2008-2010 Markus Gutschke // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -129,6 +129,7 @@ static int serverCollectHandler(struct HttpConnection *http, void *handler_) { } static void serverDestroyHandlers(void *arg, char *value) { + (void)arg; free(value); } @@ -143,6 +144,7 @@ void serverRegisterHttpHandler(struct Server *server, const char *url, h->handler = serverCollectHandler; h->arg = h; h->streamingHandler = handler; + h->websocketHandler = NULL; h->streamingArg = arg; addToTrie(&server->handlers, url, (char *)h); } @@ -158,13 +160,31 @@ void serverRegisterStreamingHttpHandler(struct Server *server, const char *url, check(h = malloc(sizeof(struct HttpHandler))); h->handler = handler; h->streamingHandler = NULL; + h->websocketHandler = NULL; h->streamingArg = NULL; h->arg = arg; addToTrie(&server->handlers, url, (char *)h); } } +void serverRegisterWebSocketHandler(struct Server *server, const char *url, + int (*handler)(struct HttpConnection *, void *, int, const char *, int), + void *arg) { + if (!handler) { + addToTrie(&server->handlers, url, NULL); + } else { + struct HttpHandler *h; + check(h = malloc(sizeof(struct HttpHandler))); + h->handler = NULL; + h->streamingHandler = NULL; + h->websocketHandler = handler; + h->arg = arg; + addToTrie(&server->handlers, url, (char *)h); + } +} + static int serverQuitHandler(struct HttpConnection *http, void *arg) { + (void)arg; httpSendReply(http, 200, "Good Bye", NO_MSG); httpExitLoop(http, 1); return HTTP_DONE; diff --git a/libhttp/server.h b/libhttp/server.h index bb879fb..8d3141f 100644 --- a/libhttp/server.h +++ b/libhttp/server.h @@ -1,5 +1,5 @@ // server.h -- Generic server that can deal with HTTP connections -// Copyright (C) 2008-2009 Markus Gutschke +// Copyright (C) 2008-2010 Markus Gutschke // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -93,6 +93,9 @@ void serverRegisterHttpHandler(struct Server *server, const char *url, void serverRegisterStreamingHttpHandler(struct Server *server, const char *url, int (*handler)(struct HttpConnection *, void *), void *arg); +void serverRegisterWebSocketHandler(struct Server *server, const char *url, + int (*handler)(struct HttpConnection *, void *, int, const char *, int), + void *arg); struct ServerConnection *serverAddConnection(struct Server *server, int fd, int (*handleConnection)(struct ServerConnection *c, void *arg, short *events, diff --git a/libhttp/ssl.c b/libhttp/ssl.c index 3c3df21..64f2158 100644 --- a/libhttp/ssl.c +++ b/libhttp/ssl.c @@ -1,5 +1,5 @@ // ssl.c -- Support functions that find and load SSL support, if available -// Copyright (C) 2008-2009 Markus Gutschke +// Copyright (C) 2008-2010 Markus Gutschke // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -288,11 +288,11 @@ static void loadSSL(void) { { { &d2i_X509 }, "d2i_X509" }, { { &X509_free }, "X509_free" } }; - for (int i = 0; i < sizeof(symbols)/sizeof(symbols[0]); i++) { + for (unsigned i = 0; i < sizeof(symbols)/sizeof(symbols[0]); i++) { if (!(*symbols[i].var = loadSymbol("libssl.so", symbols[i].fn))) { debug("Failed to load SSL support. Could not find \"%s\"", symbols[i].fn); - for (int j = 0; j < sizeof(symbols)/sizeof(symbols[0]); j++) { + for (unsigned j = 0; j < sizeof(symbols)/sizeof(symbols[0]); j++) { *symbols[j].var = NULL; } return; @@ -361,7 +361,7 @@ static const unsigned char *sslSecureReadASCIIFileToMem(int fd) { check((buf = malloc(bufSize)) != NULL); for (;;) { check(len < bufSize - 1); - size_t readLen = bufSize - len - 1; + ssize_t readLen = bufSize - len - 1; ssize_t bytesRead = NOINTR(read(fd, buf + len, readLen)); if (bytesRead > 0) { len += bytesRead; @@ -414,7 +414,7 @@ static const unsigned char *sslPEMtoASN1(const unsigned char *pem, return NULL; } unsigned char *ret; - size_t maxSize = (((end - ptr)*6)+7)/8; + ssize_t maxSize = (((end - ptr)*6)+7)/8; check((ret = malloc(maxSize)) != NULL); unsigned char *out = ret; unsigned bits = 0; @@ -542,6 +542,7 @@ static int sslSetCertificateFromFile(SSL_CTX *context, #ifdef HAVE_TLSEXT static int sslSNICallback(SSL *sslHndl, int *al, struct SSLSupport *ssl) { + (void)al; check(!ERR_peek_error()); const char *name = SSL_get_servername(sslHndl, TLSEXT_NAMETYPE_host_name); @@ -603,7 +604,7 @@ static int sslSNICallback(SSL *sslHndl, int *al, struct SSLSupport *ssl) { } free(serverName); if (context != ssl->sslContext) { - check(SSL_set_SSL_CTX(sslHndl, context) > 0); + check(SSL_set_SSL_CTX(sslHndl, context)); } check(!ERR_peek_error()); return SSL_TLSEXT_ERR_OK; @@ -616,6 +617,8 @@ static int sslSNICallback(SSL *sslHndl, int *al, struct SSLSupport *ssl) { static int gethostbyname_r(const char *name, struct hostent *ret, char *buf, size_t buflen, struct hostent **result, int *h_errnop) { + (void)buf; + (void)buflen; if (result) { *result = NULL; } diff --git a/libhttp/url.c b/libhttp/url.c index 78bcec5..088eea7 100644 --- a/libhttp/url.c +++ b/libhttp/url.c @@ -1,5 +1,5 @@ // url.c -- Object representing uniform resource locators -// Copyright (C) 2008-2009 Markus Gutschke +// Copyright (C) 2008-2010 Markus Gutschke // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -87,6 +87,7 @@ static char *urlUnescape(char *s) { } static void urlDestroyHashMapEntry(void *arg, char *key, char *value) { + (void)arg; free(key); free(value); } diff --git a/shellinabox/launcher.c b/shellinabox/launcher.c index 7c45850..f3aff9e 100644 --- a/shellinabox/launcher.c +++ b/shellinabox/launcher.c @@ -1,5 +1,5 @@ // launcher.c -- Launch services from a privileged process -// Copyright (C) 2008-2009 Markus Gutschke +// Copyright (C) 2008-2010 Markus Gutschke // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -321,7 +321,7 @@ static void loadPAM(void) { { { &pam_start }, "libpam.so", "pam_start" }, { { &misc_conv }, "libpam_misc.so", "misc_conv" } }; - for (int i = 0; i < sizeof(symbols)/sizeof(symbols[0]); i++) { + for (unsigned i = 0; i < sizeof(symbols)/sizeof(symbols[0]); i++) { if (!(*symbols[i].var = loadSymbol(symbols[i].lib, symbols[i].fn))) { #if defined(HAVE_SECURITY_PAM_CLIENT_H) if (!strcmp(symbols[i].fn, "pam_binary_handler_fn")) { @@ -336,7 +336,7 @@ static void loadPAM(void) { } debug("Failed to load PAM support. Could not find \"%s\"", symbols[i].fn); - for (int j = 0; j < sizeof(symbols)/sizeof(symbols[0]); j++) { + for (unsigned j = 0; j < sizeof(symbols)/sizeof(symbols[0]); j++) { *symbols[j].var = NULL; } break; @@ -419,7 +419,7 @@ int launchChild(int service, struct Session *session, const char *url) { } struct LaunchRequest *request; - size_t len = sizeof(struct LaunchRequest) + strlen(u) + 1; + ssize_t len = sizeof(struct LaunchRequest) + strlen(u) + 1; check(request = calloc(len, 1)); request->service = service; request->width = session->width; @@ -538,6 +538,8 @@ void deleteUtmp(struct Utmp *utmp) { } static void destroyUtmpHashEntry(void *arg, char *key, char *value) { + (void)arg; + (void)key; deleteUtmp((struct Utmp *)value); } @@ -765,6 +767,9 @@ static const struct passwd *getPWEnt(uid_t uid) { } static void sigAlrmHandler(int sig, siginfo_t *info, void *unused) { + (void)sig; + (void)info; + (void)unused; puts("\nLogin timed out after 60 seconds."); _exit(1); } @@ -958,7 +963,7 @@ static pam_handle_t *internalLogin(struct Service *service, struct Utmp *utmp, } if (restricted && - (service->uid != restricted || service->gid != pw->pw_gid)) { + (service->uid != (int)restricted || service->gid != (int)pw->pw_gid)) { puts("\nAccess denied!"); #if defined(HAVE_SECURITY_PAM_APPL_H) if (service->authUser != 2 /* SSH */) { @@ -1004,7 +1009,7 @@ static pam_handle_t *internalLogin(struct Service *service, struct Utmp *utmp, if (i == ngroups) { groups[ngroups++] = service->gid; break; - } else if (groups[i] == service->gid) { + } else if ((int)groups[i] == service->gid) { break; } } @@ -1047,6 +1052,7 @@ static pam_handle_t *internalLogin(struct Service *service, struct Utmp *utmp, } static void destroyVariableHashEntry(void *arg, char *key, char *value) { + (void)arg; free(key); free(value); } @@ -1054,6 +1060,9 @@ static void destroyVariableHashEntry(void *arg, char *key, char *value) { static void execService(int width, int height, struct Service *service, const char *peerName, char **environment, const char *url) { + (void)width; + (void)height; + // Create a hash table with all the variables that we can expand. This // includes all environment variables being passed to the child. HashMap *vars; @@ -1346,7 +1355,10 @@ static void childProcess(struct Service *service, int width, int height, pam_handle_t *pam = internalLogin(service, utmp, &environment); #if defined(HAVE_SECURITY_PAM_APPL_H) if (pam && !geteuid()) { - check(pam_open_session(pam, PAM_SILENT) == PAM_SUCCESS); + if (pam_open_session(pam, PAM_SILENT) != PAM_SUCCESS) { + fprintf(stderr, "Access denied.\n"); + _exit(1); + } pid_t pid = fork(); switch (pid) { case -1: @@ -1409,6 +1421,9 @@ static void childProcess(struct Service *service, int width, int height, } static void sigChildHandler(int sig, siginfo_t *info, void *unused) { + (void)sig; + (void)info; + (void)unused; } static void launcherDaemon(int fd) { diff --git a/shellinabox/privileges.c b/shellinabox/privileges.c index 61268a2..b7a9f6b 100644 --- a/shellinabox/privileges.c +++ b/shellinabox/privileges.c @@ -1,5 +1,5 @@ // privileges.c -- Manage process privileges -// Copyright (C) 2008-2009 Markus Gutschke +// Copyright (C) 2008-2010 Markus Gutschke // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -76,7 +76,7 @@ static void removeGroupPrivileges(int showError) { getresuid(&ru, &eu, &su); // Try to switch the user-provided group. - if ((ru && runAsGroup != rg) || + if ((ru && runAsGroup != (int)rg) || setresgid(runAsGroup, runAsGroup, runAsGroup)) { if (showError) { fatal("Only privileged users can change their group memberships"); @@ -110,7 +110,7 @@ void lowerPrivileges(void) { if (runAsUser >= 0) { // Try to switch to the user-provided user id. - if (r && runAsUser != r) { + if (r && runAsUser != (int)r) { fatal("Only privileged users can change their user id"); } check(!setresuid(runAsUser, runAsUser, -1)); @@ -136,7 +136,7 @@ void dropPrivileges(void) { if (runAsUser >= 0) { // Try to switch to the user-provided user id. - if ((r && runAsUser != r) || + if ((r && runAsUser != (int)r) || setresuid(runAsUser, runAsUser, runAsUser)) { fatal("Only privileged users can change their user id."); } diff --git a/shellinabox/service.c b/shellinabox/service.c index ca05faf..a9e9b4c 100644 --- a/shellinabox/service.c +++ b/shellinabox/service.c @@ -1,5 +1,5 @@ // service.c -- Service descriptions -// Copyright (C) 2008-2009 Markus Gutschke +// Copyright (C) 2008-2010 Markus Gutschke // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -243,9 +243,15 @@ void deleteService(struct Service *service) { } void destroyServiceHashEntry(void *arg, char *key, char *value) { + (void)arg; + (void)key; + (void)value; } static int enumerateServicesHelper(void *arg, const char *key, char **value) { + (void)arg; + (void)key; + check(services = realloc(services, ++numServices * sizeof(struct Service *))); services[numServices-1] = *(struct Service **)value; diff --git a/shellinabox/service.h b/shellinabox/service.h index 24a4281..46fd47a 100644 --- a/shellinabox/service.h +++ b/shellinabox/service.h @@ -1,5 +1,5 @@ // service.h -- Service descriptions -// Copyright (C) 2008-2009 Markus Gutschke +// Copyright (C) 2008-2010 Markus Gutschke // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -55,8 +55,8 @@ struct Service { int useHomeDir; int authUser; int useDefaultShell; - int uid; - int gid; + int uid; /* Can be -1 */ + int gid; /* Can be -1 */ const char *user; const char *group; const char *cwd; diff --git a/shellinabox/session.c b/shellinabox/session.c index d9c749c..6644f29 100644 --- a/shellinabox/session.c +++ b/shellinabox/session.c @@ -1,5 +1,5 @@ // session.c -- Session management for HTTP/HTTPS connections -// Copyright (C) 2008-2009 Markus Gutschke +// Copyright (C) 2008-2010 Markus Gutschke // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -153,6 +153,9 @@ void finishAllSessions(void) { } static void destroySessionHashEntry(void *arg, char *key, char *value) { + (void)arg; + (void)key; + deleteSession((struct Session *)value); } @@ -167,7 +170,7 @@ char *newSessionKey(void) { char *ptr = sessionKey; int count = 0; int bits = 0; - for (int i = 0;;) { + for (unsigned i = 0;;) { bits = (bits << 8) | buf[i]; count += 8; drain: diff --git a/shellinabox/shell_in_a_box.js b/shellinabox/shell_in_a_box.js index c580125..63b079a 100644 --- a/shellinabox/shell_in_a_box.js +++ b/shellinabox/shell_in_a_box.js @@ -1,5 +1,5 @@ // ShellInABox.js -- Use XMLHttpRequest to provide an AJAX terminal emulator. -// Copyright (C) 2008-2009 Markus Gutschke +// Copyright (C) 2008-2010 Markus Gutschke // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -358,8 +358,8 @@ ShellInABox.prototype.extendContextMenu = function(entries, actions) { }; ShellInABox.prototype.about = function() { - alert("Shell In A Box version " + "2.10 (revision 200)" + - "\nCopyright 2008-2009 by Markus Gutschke\n" + + alert("Shell In A Box version " + "2.10 (revision 202)" + + "\nCopyright 2008-2010 by Markus Gutschke\n" + "For more information check http://shellinabox.com" + (typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ? "\n\n" + diff --git a/shellinabox/shell_in_a_box.jspp b/shellinabox/shell_in_a_box.jspp index f862880..11a893e 100644 --- a/shellinabox/shell_in_a_box.jspp +++ b/shellinabox/shell_in_a_box.jspp @@ -1,5 +1,5 @@ // ShellInABox.js -- Use XMLHttpRequest to provide an AJAX terminal emulator. -// Copyright (C) 2008-2009 Markus Gutschke +// Copyright (C) 2008-2010 Markus Gutschke // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -359,7 +359,7 @@ ShellInABox.prototype.extendContextMenu = function(entries, actions) { ShellInABox.prototype.about = function() { alert("Shell In A Box version " + VERSION + - "\nCopyright 2008-2009 by Markus Gutschke\n" + + "\nCopyright 2008-2010 by Markus Gutschke\n" + "For more information check http://shellinabox.com" + (typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ? "\n\n" + diff --git a/shellinabox/shellinaboxd.c b/shellinabox/shellinaboxd.c index a107882..0b56377 100644 --- a/shellinabox/shellinaboxd.c +++ b/shellinabox/shellinaboxd.c @@ -1,6 +1,6 @@ // shellinaboxd.c -- A custom web server that makes command line applications // available as AJAX web applications. -// Copyright (C) 2008-2009 Markus Gutschke +// Copyright (C) 2008-2010 Markus Gutschke // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -257,6 +257,7 @@ static void sessionDone(void *arg) { static int handleSession(struct ServerConnection *connection, void *arg, short *events, short revents) { + (void)events; struct Session *session = (struct Session *)arg; session->connection = connection; int len = MAX_RESPONSE - session->len; @@ -311,6 +312,7 @@ static int invalidatePendingHttpSession(void *arg, const char *key, static int dataHandler(HttpConnection *http, struct Service *service, const char *buf, int len, URL *url) { + (void)len; if (!buf) { // Somebody unexpectedly closed our http connection (e.g. because of a // timeout). This is the last notification that we will get. @@ -514,13 +516,13 @@ static void serveStaticFile(HttpConnection *http, const char *contentType, // Remember the beginning of the "[if ...]" statement ifPtr = ptr; } - } else if (ifPtr && !elsePtr && eol - ptr >= strlen(tag) + 7 && + } else if (ifPtr && !elsePtr && eol - ptr >= (ssize_t)strlen(tag) + 7 && !memcmp(ptr, "[else ", 6) && !memcmp(ptr + 6, tag, strlen(tag)) && ptr[6 + strlen(tag)] == ']') { // Found an "[else ...]" statement. Remember where it started. elsePtr = ptr; - } else if (ifPtr && eol - ptr >= strlen(tag) + 8 && + } else if (ifPtr && eol - ptr >= (ssize_t)strlen(tag) + 8 && !memcmp(ptr, "[endif ", 7) && !memcmp(ptr + 7, tag, strlen(tag)) && ptr[7 + strlen(tag)] == ']') { @@ -810,6 +812,7 @@ static void usage(void) { } static void destroyExternalFileHashEntry(void *arg, char *key, char *value) { + (void)arg; free(key); free(value); } @@ -938,7 +941,7 @@ static void parseArgs(int argc, char * const argv[]) { st.st_size + 2)); char *newData = strrchr(cssStyleSheet, '\000'); *newData++ = '\n'; - if (fread(newData, 1, st.st_size, css) != st.st_size) { + if (fread(newData, st.st_size, 1, css) != 1) { fatal("Failed to read style sheet \"%s\"", optarg); } newData[st.st_size]= '\000'; @@ -1149,7 +1152,7 @@ static void parseArgs(int argc, char * const argv[]) { static void removeLimits() { static int res[] = { RLIMIT_CPU, RLIMIT_DATA, RLIMIT_FSIZE, RLIMIT_NPROC }; - for (int i = 0; i < sizeof(res)/sizeof(int); i++) { + for (unsigned i = 0; i < sizeof(res)/sizeof(int); i++) { struct rlimit rl; getrlimit(res[i], &rl); if (rl.rlim_max < RLIM_INFINITY) { diff --git a/shellinabox/usercss.c b/shellinabox/usercss.c index e94be74..6be9b3e 100644 --- a/shellinabox/usercss.c +++ b/shellinabox/usercss.c @@ -1,5 +1,5 @@ // usercss.c -- Defines user-selectable CSS options -// Copyright (C) 2008-2009 Markus Gutschke +// Copyright (C) 2008-2010 Markus Gutschke // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -60,6 +60,9 @@ static struct HashMap *defines; static void definesDestructor(void *arg, char *key, char *value) { + (void)arg; + (void)value; + free(key); } @@ -73,7 +76,7 @@ static void readStylesheet(struct UserCSS *userCSS, const char *filename, FILE *fp; check(fp = fdopen(fd, "r")); check(*style = malloc(st.st_size + 1)); - check(fread(*style, 1, st.st_size, fp) == st.st_size); + check(fread(*style, st.st_size, 1, fp) == 1); (*style)[st.st_size] = '\000'; *len = st.st_size; fclose(fp); diff --git a/shellinabox/vt100.js b/shellinabox/vt100.js index c46e0c0..e6fbf79 100644 --- a/shellinabox/vt100.js +++ b/shellinabox/vt100.js @@ -1,5 +1,5 @@ // VT100.js -- JavaScript based terminal emulator -// Copyright (C) 2008-2009 Markus Gutschke +// Copyright (C) 2008-2010 Markus Gutschke // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -1955,8 +1955,8 @@ VT100.prototype.toggleBell = function() { }; VT100.prototype.about = function() { - alert("VT100 Terminal Emulator " + "2.10 (revision 200)" + - "\nCopyright 2008-2009 by Markus Gutschke\n" + + alert("VT100 Terminal Emulator " + "2.10 (revision 202)" + + "\nCopyright 2008-2010 by Markus Gutschke\n" + "For more information check http://shellinabox.com"); }; diff --git a/shellinabox/vt100.jspp b/shellinabox/vt100.jspp index bbbadca..4d83daa 100644 --- a/shellinabox/vt100.jspp +++ b/shellinabox/vt100.jspp @@ -1,5 +1,5 @@ // VT100.js -- JavaScript based terminal emulator -// Copyright (C) 2008-2009 Markus Gutschke +// Copyright (C) 2008-2010 Markus Gutschke // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -1956,7 +1956,7 @@ VT100.prototype.toggleBell = function() { VT100.prototype.about = function() { alert("VT100 Terminal Emulator " + VERSION + - "\nCopyright 2008-2009 by Markus Gutschke\n" + + "\nCopyright 2008-2010 by Markus Gutschke\n" + "For more information check http://shellinabox.com"); };