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
This commit is contained in:
zodiac@gmail.com 2010-03-29 16:40:17 +00:00
parent 5a75b2f091
commit 9b0a937e35
29 changed files with 581 additions and 138 deletions

View file

@ -1,4 +1,4 @@
Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
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

View file

@ -1,3 +1,12 @@
2010-03-29 Markus Gutschke <markus@shellinabox.com>
* 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 <markus@shellinabox.com>
* Add .note.GNU-stack to all object files so that the generated

View file

@ -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

View file

@ -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.

View file

@ -25,6 +25,9 @@
/* Define to 1 if you have the <inttypes.h> 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 <libutil.h> 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"

View file

@ -24,6 +24,9 @@
/* Define to 1 if you have the <inttypes.h> 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 <libutil.h> header file. */
#undef HAVE_LIBUTIL_H

21
configure vendored
View file

@ -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 <math.h>
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

View file

@ -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 <pthread.h>
[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 <math.h>],
[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,

2
debian/copyright vendored
View file

@ -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

View file

@ -1,5 +1,5 @@
// VT100.js -- JavaScript based terminal emulator
// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// 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");
};

View file

@ -1,5 +1,5 @@
// http.h -- Library for implementing embedded custom HTTP servers
// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// 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 *);

View file

@ -1,5 +1,5 @@
// httpconnection.c -- Manage state machine for HTTP connections
// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// 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);
}

View file

@ -1,5 +1,5 @@
// httpconnection.h -- Manage state machine for HTTP connections
// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// 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*);

View file

@ -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

View file

@ -1,5 +1,5 @@
// server.c -- Generic server that can deal with HTTP connections
// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// 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;

View file

@ -1,5 +1,5 @@
// server.h -- Generic server that can deal with HTTP connections
// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// 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,

View file

@ -1,5 +1,5 @@
// ssl.c -- Support functions that find and load SSL support, if available
// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// 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;
}

View file

@ -1,5 +1,5 @@
// url.c -- Object representing uniform resource locators
// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// 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);
}

View file

@ -1,5 +1,5 @@
// launcher.c -- Launch services from a privileged process
// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// 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) {

View file

@ -1,5 +1,5 @@
// privileges.c -- Manage process privileges
// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// 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.");
}

View file

@ -1,5 +1,5 @@
// service.c -- Service descriptions
// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// 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;

View file

@ -1,5 +1,5 @@
// service.h -- Service descriptions
// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// 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;

View file

@ -1,5 +1,5 @@
// session.c -- Session management for HTTP/HTTPS connections
// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// 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:

View file

@ -1,5 +1,5 @@
// ShellInABox.js -- Use XMLHttpRequest to provide an AJAX terminal emulator.
// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// 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" +

View file

@ -1,5 +1,5 @@
// ShellInABox.js -- Use XMLHttpRequest to provide an AJAX terminal emulator.
// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// 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" +

View file

@ -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 <markus@shellinabox.com>
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// 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) {

View file

@ -1,5 +1,5 @@
// usercss.c -- Defines user-selectable CSS options
// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// 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);

View file

@ -1,5 +1,5 @@
// VT100.js -- JavaScript based terminal emulator
// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// 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");
};

View file

@ -1,5 +1,5 @@
// VT100.js -- JavaScript based terminal emulator
// Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// 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");
};