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 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 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> 2009-12-10 Markus Gutschke <markus@shellinabox.com>
* Add .note.GNU-stack to all object files so that the generated * 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_CFLAGS = -g -std=gnu99 -Wall
AM_LDFLAGS = -g AM_LDFLAGS = -g
OBJCOPY ?= objcopy
noinst_LTLIBRARIES = libhttp.la \ noinst_LTLIBRARIES = libhttp.la \
liblogging.la liblogging.la
noinst_DATA = $(top_srcdir)/demo/demo.js noinst_DATA = $(top_srcdir)/demo/demo.js
@ -234,37 +236,37 @@ clean-local:
-rm -rf GNU-stack -rm -rf GNU-stack
.css.o: .css.o:
@echo objcopy "$<" "$@" @echo $(OBJCOPY) "$<" "$@"
@objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@" "$<" "$@"
@-printf '\000' >GNU-stack && \ @-printf '\000' >GNU-stack && \
objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack rm -f GNU-stack
.gif.o: .gif.o:
@echo objcopy "$<" "$@" @echo $(OBJCOPY) "$<" "$@"
@objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@" "$<" "$@"
@-printf '\000' >GNU-stack && \ @-printf '\000' >GNU-stack && \
objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack rm -f GNU-stack
.html.o: .html.o:
@echo objcopy "$<" "$@" @echo $(OBJCOPY) "$<" "$@"
@objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@" "$<" "$@"
@-printf '\000' >GNU-stack && \ @-printf '\000' >GNU-stack && \
objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack rm -f GNU-stack
.ico.o: .ico.o:
@echo objcopy "$<" "$@" @echo $(OBJCOPY) "$<" "$@"
@objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@" "$<" "$@"
@-printf '\000' >GNU-stack && \ @-printf '\000' >GNU-stack && \
objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f 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: .js.o:
@echo objcopy "$<" "$@" @echo $(OBJCOPY) "$<" "$@"
@objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@" "$<" "$@"
@-printf '\000' >GNU-stack && \ @-printf '\000' >GNU-stack && \
objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack rm -f GNU-stack
.wav.o: .wav.o:
@echo objcopy "$<" "$@" @echo $(OBJCOPY) "$<" "$@"
@objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@" "$<" "$@"
@-printf '\000' >GNU-stack && \ @-printf '\000' >GNU-stack && \
objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack rm -f GNU-stack

View file

@ -1152,6 +1152,8 @@ uninstall-man: uninstall-man1
uninstall-man uninstall-man1 uninstall-man uninstall-man1
OBJCOPY ?= objcopy
libtool: $(LIBTOOL_DEPS) libtool: $(LIBTOOL_DEPS)
$(SHELL) ./config.status --recheck $(SHELL) ./config.status --recheck
@ -1247,35 +1249,35 @@ clean-local:
-rm -rf GNU-stack -rm -rf GNU-stack
.css.o: .css.o:
@echo objcopy "$<" "$@" @echo $(OBJCOPY) "$<" "$@"
@objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@" "$<" "$@"
@-printf '\000' >GNU-stack && \ @-printf '\000' >GNU-stack && \
objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack rm -f GNU-stack
.gif.o: .gif.o:
@echo objcopy "$<" "$@" @echo $(OBJCOPY) "$<" "$@"
@objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@" "$<" "$@"
@-printf '\000' >GNU-stack && \ @-printf '\000' >GNU-stack && \
objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack rm -f GNU-stack
.html.o: .html.o:
@echo objcopy "$<" "$@" @echo $(OBJCOPY) "$<" "$@"
@objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@" "$<" "$@"
@-printf '\000' >GNU-stack && \ @-printf '\000' >GNU-stack && \
objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack rm -f GNU-stack
.ico.o: .ico.o:
@echo objcopy "$<" "$@" @echo $(OBJCOPY) "$<" "$@"
@objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@" "$<" "$@"
@-printf '\000' >GNU-stack && \ @-printf '\000' >GNU-stack && \
objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack rm -f GNU-stack
shellinabox/shell_in_a_box.o: shellinabox/shell_in_a_box.js config.h 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: .js.o:
@echo objcopy "$<" "$@" @echo $(OBJCOPY) "$<" "$@"
@objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@" "$<" "$@"
@-printf '\000' >GNU-stack && \ @-printf '\000' >GNU-stack && \
objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack rm -f GNU-stack
.wav.o: .wav.o:
@echo objcopy "$<" "$@" @echo $(OBJCOPY) "$<" "$@"
@objcopy -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)` \ @$(OBJCOPY) -I binary `$(objcopyflags)` `echo "$<" | $(renamesymbols)`\
"$<" "$@" "$<" "$@"
@-printf '\000' >GNU-stack && \ @-printf '\000' >GNU-stack && \
objcopy --add-section .note.GNU-stack=GNU-stack "$@"; \ $(OBJCOPY) --add-section .note.GNU-stack=GNU-stack "$@"; \
rm -f GNU-stack rm -f GNU-stack
# Tell versions [3.59,3.63) of GNU make to not export all variables. # 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 to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1 #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. */ /* Define to 1 if you have the <libutil.h> header file. */
/* #undef HAVE_LIBUTIL_H */ /* #undef HAVE_LIBUTIL_H */
@ -141,7 +144,7 @@
#define STDC_HEADERS 1 #define STDC_HEADERS 1
/* Most recent revision number in the version control system */ /* Most recent revision number in the version control system */
#define VCS_REVISION "200" #define VCS_REVISION "202"
/* Version number of package */ /* Version number of package */
#define VERSION "2.10" #define VERSION "2.10"

View file

@ -24,6 +24,9 @@
/* Define to 1 if you have the <inttypes.h> header file. */ /* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H #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. */ /* Define to 1 if you have the <libutil.h> header file. */
#undef HAVE_LIBUTIL_H #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 ac_compiler_gnu=$ac_cv_c_compiler_gnu
VCS_REVISION=200 VCS_REVISION=202
cat >>confdefs.h <<_ACEOF cat >>confdefs.h <<_ACEOF
@ -10682,6 +10682,25 @@ if ac_fn_c_try_link "$LINENO"; then :
$as_echo "#define HAVE_SIGWAIT 1" >>confdefs.h $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 fi
rm -f core conftest.err conftest.$ac_objext \ rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext 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 dnl This is the one location where the authoritative version number is stored
AC_INIT(shellinabox, 2.10, markus@shellinabox.com) AC_INIT(shellinabox, 2.10, markus@shellinabox.com)
VCS_REVISION=200 VCS_REVISION=202
AC_SUBST(VCS_REVISION) AC_SUBST(VCS_REVISION)
AC_DEFINE_UNQUOTED(VCS_REVISION, "${VCS_REVISION}", AC_DEFINE_UNQUOTED(VCS_REVISION, "${VCS_REVISION}",
[Most recent revision number in the version control system]) [Most recent revision number in the version control system])
@ -55,6 +55,12 @@ AC_TRY_LINK([#include <pthread.h>
[AC_DEFINE(HAVE_SIGWAIT, 1, [AC_DEFINE(HAVE_SIGWAIT, 1,
Define to 1 if you have a working sigwait)]) 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 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. dnl feature, if the user tells us that it does not do the right thing.
AC_ARG_ENABLE(login, 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 Upstream Author: markus@shellinabox.com
Copyright (c) 2008-2009, Markus Gutschke Copyright (c) 2008-2010, Markus Gutschke
All rights reserved. All rights reserved.
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify

View file

@ -1,5 +1,5 @@
// VT100.js -- JavaScript based terminal emulator // 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 // 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 // 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() { VT100.prototype.about = function() {
alert("VT100 Terminal Emulator " + "2.10 (revision 200)" + alert("VT100 Terminal Emulator " + "2.10 (revision 202)" +
"\nCopyright 2008-2009 by Markus Gutschke\n" + "\nCopyright 2008-2010 by Markus Gutschke\n" +
"For more information check http://shellinabox.com"); "For more information check http://shellinabox.com");
}; };

View file

@ -1,5 +1,5 @@
// http.h -- Library for implementing embedded custom HTTP servers // 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 // 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 // it under the terms of the GNU General Public License version 2 as
@ -56,7 +56,13 @@
#define HTTP_SUSPEND 3 #define HTTP_SUSPEND 3
#define HTTP_PARTIAL_REPLY 4 #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 NO_MSG "\001"
#define BINARY_MSG "\001%d%p"
#define NOINTR(x) ({ int i__; while ((i__ = (x)) < 0 && errno == EINTR); i__;}) #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, void serverRegisterStreamingHttpHandler(Server *server, const char *url,
int (*handler)(HttpConnection *, void *), int (*handler)(HttpConnection *, void *),
void *arg); 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, ServerConnection *serverAddConnection(Server *server, int fd,
int (*handleConnection)(ServerConnection *, int (*handleConnection)(ServerConnection *,
void *arg, short *events, void *arg, short *events,
@ -109,6 +118,10 @@ void *httpSetPrivate(HttpConnection *http, void *private);
void httpSendReply(HttpConnection *http, int code, void httpSendReply(HttpConnection *http, int code,
const char *msg, const char *fmt, ...) const char *msg, const char *fmt, ...)
__attribute__((format(printf, 4, 5))); __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); void httpExitLoop(HttpConnection *http, int exitAll);
Server *httpGetServer(const HttpConnection *http); Server *httpGetServer(const HttpConnection *http);
ServerConnection *httpGetServerConnection(const HttpConnection *); ServerConnection *httpGetServerConnection(const HttpConnection *);

View file

@ -1,5 +1,5 @@
// httpconnection.c -- Manage state machine for HTTP connections // 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 // 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 // it under the terms of the GNU General Public License version 2 as
@ -66,6 +66,9 @@
#ifdef HAVE_STRLCAT #ifdef HAVE_STRLCAT
#define strncat(a,b,c) ({ char *_a = (a); strlcat(_a, (b), (c)+1); _a; }) #define strncat(a,b,c) ({ char *_a = (a); strlcat(_a, (b), (c)+1); _a; })
#endif #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); \ #define max(a, b) ({ typeof(a) _a = (a); typeof(b) _b = (b); \
_a > _b ? _a : _b; }) _a > _b ? _a : _b; })
@ -223,8 +226,9 @@ static char *strcasestr(const char *haystack, const char *needle) {
static int httpFinishCommand(struct HttpConnection *http) { static int httpFinishCommand(struct HttpConnection *http) {
int rc = HTTP_DONE; int rc = HTTP_DONE;
if (http->callback && !http->done) { if ((http->callback || http->websocketHandler) && !http->done) {
rc = http->callback(http, http->arg, NULL, 0); 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_SUSPEND);
check(rc != HTTP_PARTIAL_REPLY); check(rc != HTTP_PARTIAL_REPLY);
http->callback = NULL; http->callback = NULL;
@ -267,6 +271,7 @@ static int httpFinishCommand(struct HttpConnection *http) {
} }
static void httpDestroyHeaders(void *arg, char *key, char *value) { static void httpDestroyHeaders(void *arg, char *key, char *value) {
(void)arg;
free(key); free(key);
free(value); free(value);
} }
@ -296,7 +301,7 @@ static char *getPeerName(int fd, int *port, int numericHosts) {
} }
static void httpSetState(struct HttpConnection *http, int state) { static void httpSetState(struct HttpConnection *http, int state) {
if (state == http->state) { if (state == (int)http->state) {
return; return;
} }
@ -372,7 +377,9 @@ void initHttpConnection(struct HttpConnection *http, struct Server *server,
http->msgOffset = 0; http->msgOffset = 0;
http->totalWritten = 0; http->totalWritten = 0;
http->expecting = 0; http->expecting = 0;
http->websocketType = WS_UNDEFINED;
http->callback = NULL; http->callback = NULL;
http->websocketHandler = NULL;
http->arg = NULL; http->arg = NULL;
http->private = NULL; http->private = NULL;
http->code = 200; http->code = 200;
@ -388,8 +395,12 @@ void initHttpConnection(struct HttpConnection *http, struct Server *server,
void destroyHttpConnection(struct HttpConnection *http) { void destroyHttpConnection(struct HttpConnection *http) {
if (http) { if (http) {
if (http->isSuspended || http->isPartialReply) { if (http->isSuspended || http->isPartialReply) {
if (http->callback && !http->done) { if (!http->done) {
if (http->callback) {
http->callback(http, http->arg, NULL, 0); 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->callback = NULL;
http->isSuspended = 0; http->isSuspended = 0;
@ -823,23 +834,86 @@ static int httpHandleCommand(struct HttpConnection *http,
const char *host = getFromHashMap(&http->header, const char *host = getFromHashMap(&http->header,
"host"); "host");
if (host) { if (host) {
for (char ch; (ch = *host) != '\000'; host++) { for (char ch, *ptr = (char *)host; (ch = *ptr) != '\000'; ptr++) {
if (ch == ':') { if (ch == ':') {
*(char *)host = '\000'; *ptr = '\000';
break; break;
} }
if (ch != '-' && ch != '.' && if (ch != '-' && ch != '.' &&
(ch < '0' ||(ch > '9' && ch < 'A') || (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); httpSendReply(http, 400, "Bad Request", NO_MSG);
return HTTP_DONE; return HTTP_DONE;
} }
} }
} }
char *diff; char *diff;
struct HttpHandler *h = (struct HttpHandler *)getFromTrie(handlers, struct HttpHandler *h = (struct HttpHandler *)getFromTrie(handlers,
http->path, &diff); http->path, &diff);
if (h) { if (h) {
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;
}
}
}
}
bad_ws_upgrade:;
if (h->handler) {
check(diff); check(diff);
while (diff > http->path && diff[-1] == '/') { while (diff > http->path && diff[-1] == '/') {
diff--; diff--;
@ -868,6 +942,7 @@ static int httpHandleCommand(struct HttpConnection *http,
return h->handler(http, h->arg); return h->handler(http, h->arg);
} }
} }
}
httpSendReply(http, 404, "File Not Found", NO_MSG); httpSendReply(http, 404, "File Not Found", NO_MSG);
return HTTP_DONE; return HTTP_DONE;
} }
@ -1011,13 +1086,20 @@ static int httpParseHeaders(struct HttpConnection *http,
serverSetTimeout(connection, CONNECTION_TIMEOUT); serverSetTimeout(connection, CONNECTION_TIMEOUT);
} }
check(!http->done); check(!http->done);
if (!http->expecting && http->callback) { if (!http->expecting) {
if (http->callback) {
rc = http->callback(http, http->arg, "", 0); rc = http->callback(http, http->arg, "", 0);
if (rc != HTTP_READ_MORE) { if (rc != HTTP_READ_MORE) {
goto retry; goto retry;
} }
} else if (http->websocketHandler) {
http->websocketHandler(http, http->arg, WS_CONNECTION_OPENED,
NULL, 0);
} }
}
if (http->state != WEBSOCKET) {
httpSetState(http, http->expecting ? PAYLOAD : COMMAND); httpSetState(http, http->expecting ? PAYLOAD : COMMAND);
}
break; break;
case HTTP_SUSPEND: case HTTP_SUSPEND:
http->isSuspended = 1; http->isSuspended = 1;
@ -1187,6 +1269,140 @@ static int httpParsePayload(struct HttpConnection *http, int offset,
return consumed; 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_, int httpHandleConnection(struct ServerConnection *connection, void *http_,
short *events, short revents) { short *events, short revents) {
struct HttpConnection *http = (struct HttpConnection *)http_; struct HttpConnection *http = (struct HttpConnection *)http_;
@ -1219,6 +1435,7 @@ int httpHandleConnection(struct ServerConnection *connection, void *http_,
bytes = 0; bytes = 0;
} }
} }
if (bytes > 0 && http->state == SNIFFING_SSL) { if (bytes > 0 && http->state == SNIFFING_SSL) {
// Assume that all legitimate HTTP commands start with a sequence of // Assume that all legitimate HTTP commands start with a sequence of
// letters followed by a space character. If we don't see this pattern, // 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"); strcmp(method, "CONNECT");
http->state = COMMAND; http->state = COMMAND;
break; break;
} else if (j >= sizeof(method)-1 || } else if (j >= (int)sizeof(method)-1 ||
ch < 'A' || (ch > 'Z' && ch < 'a') || ch > 'z') { ch < 'A' || (ch > 'Z' && ch < 'a') || ch > 'z') {
isSSL = 1; isSSL = 1;
http->state = COMMAND; http->state = COMMAND;
@ -1262,6 +1479,7 @@ int httpHandleConnection(struct ServerConnection *connection, void *http_,
} }
} }
} }
if (bytes > 0 || (eof && http->partial)) { if (bytes > 0 || (eof && http->partial)) {
check(!!http->partial == !!http->partialLength); check(!!http->partial == !!http->partialLength);
int offset = -http->partialLength; int offset = -http->partialLength;
@ -1331,7 +1549,16 @@ int httpHandleConnection(struct ServerConnection *connection, void *http_,
consumed = len; consumed = len;
pushBack = bytes - offset - 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) { if (pushBack) {
check(offset + pushBack == bytes); check(offset + pushBack == bytes);
if (offset >= 0) { if (offset >= 0) {
@ -1383,6 +1610,7 @@ int httpHandleConnection(struct ServerConnection *connection, void *http_,
break; break;
case PAYLOAD: case PAYLOAD:
case DISCARD_PAYLOAD: case DISCARD_PAYLOAD:
case WEBSOCKET:
http->expecting = 0; http->expecting = 0;
httpCloseRead(http); httpCloseRead(http);
httpSetState(http, COMMAND); 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) { void httpExitLoop(struct HttpConnection *http, int exitAll) {
serverExitLoop(http->server, exitAll); serverExitLoop(http->server, exitAll);
} }

View file

@ -1,5 +1,5 @@
// httpconnection.h -- Manage state machine for HTTP connections // 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 // 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 // it under the terms of the GNU General Public License version 2 as
@ -60,6 +60,10 @@
#define HTTP_SUSPEND 3 #define HTTP_SUSPEND 3
#define HTTP_PARTIAL_REPLY 4 #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" #define NO_MSG "\001"
struct HttpConnection { struct HttpConnection {
@ -71,7 +75,8 @@ struct HttpConnection {
int isSuspended; int isSuspended;
int isPartialReply; int isPartialReply;
int done; int done;
enum { SNIFFING_SSL, COMMAND, HEADERS, PAYLOAD, DISCARD_PAYLOAD } state; enum { SNIFFING_SSL, COMMAND, HEADERS, PAYLOAD, DISCARD_PAYLOAD,
WEBSOCKET } state;
char *peerName; char *peerName;
int peerPort; int peerPort;
char *url; char *url;
@ -91,8 +96,11 @@ struct HttpConnection {
int msgOffset; int msgOffset;
int totalWritten; int totalWritten;
int expecting; int expecting;
int websocketType;
int (*callback)(struct HttpConnection *, void *, int (*callback)(struct HttpConnection *, void *,
const char *,int); const char *,int);
int (*websocketHandler)(struct HttpConnection *, void *,
int, const char *, int);
void *arg; void *arg;
void *private; void *private;
int code; int code;
@ -104,6 +112,8 @@ struct HttpConnection {
struct HttpHandler { struct HttpHandler {
int (*handler)(struct HttpConnection *, void *); int (*handler)(struct HttpConnection *, void *);
int (*streamingHandler)(struct HttpConnection *, void *, const char *, int); int (*streamingHandler)(struct HttpConnection *, void *, const char *, int);
int (*websocketHandler)(struct HttpConnection *, void *, int,
const char *, int);
void *arg, *streamingArg; void *arg, *streamingArg;
}; };
@ -128,6 +138,11 @@ void *httpSetPrivate(struct HttpConnection *http, void *private);
void httpSendReply(struct HttpConnection *http, int code, void httpSendReply(struct HttpConnection *http, int code,
const char *msg, const char *fmt, ...) const char *msg, const char *fmt, ...)
__attribute__((format(printf, 4, 5))); __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); void httpExitLoop(struct HttpConnection *http, int exitAll);
struct Server *httpGetServer(const struct HttpConnection *http); struct Server *httpGetServer(const struct HttpConnection *http);
struct ServerConnection *httpGetServerConnection(const struct HttpConnection*); struct ServerConnection *httpGetServerConnection(const struct HttpConnection*);

View file

@ -5,6 +5,7 @@ serverGetListeningPort
serverGetFd serverGetFd
serverRegisterHttpHandler serverRegisterHttpHandler
serverRegisterStreamingHttpHandler serverRegisterStreamingHttpHandler
serverRegisterWebSocketHandler
serverAddConnection serverAddConnection
serverDeleteConnection serverDeleteConnection
serverSetTimeout serverSetTimeout
@ -24,6 +25,8 @@ httpSetCallback
httpGetPrivate httpGetPrivate
httpSetPrivate httpSetPrivate
httpSendReply httpSendReply
httpSendWebSocketTextMsg
httpSendWebSocketBinaryMsg
httpExitLoop httpExitLoop
httpGetServer httpGetServer
httpGetServerConnection httpGetServerConnection

View file

@ -1,5 +1,5 @@
// server.c -- Generic server that can deal with HTTP connections // 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 // 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 // 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) { static void serverDestroyHandlers(void *arg, char *value) {
(void)arg;
free(value); free(value);
} }
@ -143,6 +144,7 @@ void serverRegisterHttpHandler(struct Server *server, const char *url,
h->handler = serverCollectHandler; h->handler = serverCollectHandler;
h->arg = h; h->arg = h;
h->streamingHandler = handler; h->streamingHandler = handler;
h->websocketHandler = NULL;
h->streamingArg = arg; h->streamingArg = arg;
addToTrie(&server->handlers, url, (char *)h); addToTrie(&server->handlers, url, (char *)h);
} }
@ -158,13 +160,31 @@ void serverRegisterStreamingHttpHandler(struct Server *server, const char *url,
check(h = malloc(sizeof(struct HttpHandler))); check(h = malloc(sizeof(struct HttpHandler)));
h->handler = handler; h->handler = handler;
h->streamingHandler = NULL; h->streamingHandler = NULL;
h->websocketHandler = NULL;
h->streamingArg = NULL; h->streamingArg = NULL;
h->arg = arg; h->arg = arg;
addToTrie(&server->handlers, url, (char *)h); 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) { static int serverQuitHandler(struct HttpConnection *http, void *arg) {
(void)arg;
httpSendReply(http, 200, "Good Bye", NO_MSG); httpSendReply(http, 200, "Good Bye", NO_MSG);
httpExitLoop(http, 1); httpExitLoop(http, 1);
return HTTP_DONE; return HTTP_DONE;

View file

@ -1,5 +1,5 @@
// server.h -- Generic server that can deal with HTTP connections // 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 // 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 // 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, void serverRegisterStreamingHttpHandler(struct Server *server, const char *url,
int (*handler)(struct HttpConnection *, void *), int (*handler)(struct HttpConnection *, void *),
void *arg); 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, struct ServerConnection *serverAddConnection(struct Server *server, int fd,
int (*handleConnection)(struct ServerConnection *c, int (*handleConnection)(struct ServerConnection *c,
void *arg, short *events, void *arg, short *events,

View file

@ -1,5 +1,5 @@
// ssl.c -- Support functions that find and load SSL support, if available // 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 // 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 // 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" }, { { &d2i_X509 }, "d2i_X509" },
{ { &X509_free }, "X509_free" } { { &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))) { if (!(*symbols[i].var = loadSymbol("libssl.so", symbols[i].fn))) {
debug("Failed to load SSL support. Could not find \"%s\"", debug("Failed to load SSL support. Could not find \"%s\"",
symbols[i].fn); 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; *symbols[j].var = NULL;
} }
return; return;
@ -361,7 +361,7 @@ static const unsigned char *sslSecureReadASCIIFileToMem(int fd) {
check((buf = malloc(bufSize)) != NULL); check((buf = malloc(bufSize)) != NULL);
for (;;) { for (;;) {
check(len < bufSize - 1); 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)); ssize_t bytesRead = NOINTR(read(fd, buf + len, readLen));
if (bytesRead > 0) { if (bytesRead > 0) {
len += bytesRead; len += bytesRead;
@ -414,7 +414,7 @@ static const unsigned char *sslPEMtoASN1(const unsigned char *pem,
return NULL; return NULL;
} }
unsigned char *ret; unsigned char *ret;
size_t maxSize = (((end - ptr)*6)+7)/8; ssize_t maxSize = (((end - ptr)*6)+7)/8;
check((ret = malloc(maxSize)) != NULL); check((ret = malloc(maxSize)) != NULL);
unsigned char *out = ret; unsigned char *out = ret;
unsigned bits = 0; unsigned bits = 0;
@ -542,6 +542,7 @@ static int sslSetCertificateFromFile(SSL_CTX *context,
#ifdef HAVE_TLSEXT #ifdef HAVE_TLSEXT
static int sslSNICallback(SSL *sslHndl, int *al, struct SSLSupport *ssl) { static int sslSNICallback(SSL *sslHndl, int *al, struct SSLSupport *ssl) {
(void)al;
check(!ERR_peek_error()); check(!ERR_peek_error());
const char *name = SSL_get_servername(sslHndl, const char *name = SSL_get_servername(sslHndl,
TLSEXT_NAMETYPE_host_name); TLSEXT_NAMETYPE_host_name);
@ -603,7 +604,7 @@ static int sslSNICallback(SSL *sslHndl, int *al, struct SSLSupport *ssl) {
} }
free(serverName); free(serverName);
if (context != ssl->sslContext) { if (context != ssl->sslContext) {
check(SSL_set_SSL_CTX(sslHndl, context) > 0); check(SSL_set_SSL_CTX(sslHndl, context));
} }
check(!ERR_peek_error()); check(!ERR_peek_error());
return SSL_TLSEXT_ERR_OK; 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, static int gethostbyname_r(const char *name, struct hostent *ret,
char *buf, size_t buflen, char *buf, size_t buflen,
struct hostent **result, int *h_errnop) { struct hostent **result, int *h_errnop) {
(void)buf;
(void)buflen;
if (result) { if (result) {
*result = NULL; *result = NULL;
} }

View file

@ -1,5 +1,5 @@
// url.c -- Object representing uniform resource locators // 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 // 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 // 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) { static void urlDestroyHashMapEntry(void *arg, char *key, char *value) {
(void)arg;
free(key); free(key);
free(value); free(value);
} }

View file

@ -1,5 +1,5 @@
// launcher.c -- Launch services from a privileged process // 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 // 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 // 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" }, { { &pam_start }, "libpam.so", "pam_start" },
{ { &misc_conv }, "libpam_misc.so", "misc_conv" } { { &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 (!(*symbols[i].var = loadSymbol(symbols[i].lib, symbols[i].fn))) {
#if defined(HAVE_SECURITY_PAM_CLIENT_H) #if defined(HAVE_SECURITY_PAM_CLIENT_H)
if (!strcmp(symbols[i].fn, "pam_binary_handler_fn")) { 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\"", debug("Failed to load PAM support. Could not find \"%s\"",
symbols[i].fn); 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; *symbols[j].var = NULL;
} }
break; break;
@ -419,7 +419,7 @@ int launchChild(int service, struct Session *session, const char *url) {
} }
struct LaunchRequest *request; 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)); check(request = calloc(len, 1));
request->service = service; request->service = service;
request->width = session->width; request->width = session->width;
@ -538,6 +538,8 @@ void deleteUtmp(struct Utmp *utmp) {
} }
static void destroyUtmpHashEntry(void *arg, char *key, char *value) { static void destroyUtmpHashEntry(void *arg, char *key, char *value) {
(void)arg;
(void)key;
deleteUtmp((struct Utmp *)value); 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) { static void sigAlrmHandler(int sig, siginfo_t *info, void *unused) {
(void)sig;
(void)info;
(void)unused;
puts("\nLogin timed out after 60 seconds."); puts("\nLogin timed out after 60 seconds.");
_exit(1); _exit(1);
} }
@ -958,7 +963,7 @@ static pam_handle_t *internalLogin(struct Service *service, struct Utmp *utmp,
} }
if (restricted && if (restricted &&
(service->uid != restricted || service->gid != pw->pw_gid)) { (service->uid != (int)restricted || service->gid != (int)pw->pw_gid)) {
puts("\nAccess denied!"); puts("\nAccess denied!");
#if defined(HAVE_SECURITY_PAM_APPL_H) #if defined(HAVE_SECURITY_PAM_APPL_H)
if (service->authUser != 2 /* SSH */) { if (service->authUser != 2 /* SSH */) {
@ -1004,7 +1009,7 @@ static pam_handle_t *internalLogin(struct Service *service, struct Utmp *utmp,
if (i == ngroups) { if (i == ngroups) {
groups[ngroups++] = service->gid; groups[ngroups++] = service->gid;
break; break;
} else if (groups[i] == service->gid) { } else if ((int)groups[i] == service->gid) {
break; 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) { static void destroyVariableHashEntry(void *arg, char *key, char *value) {
(void)arg;
free(key); free(key);
free(value); 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, static void execService(int width, int height, struct Service *service,
const char *peerName, char **environment, const char *peerName, char **environment,
const char *url) { const char *url) {
(void)width;
(void)height;
// Create a hash table with all the variables that we can expand. This // Create a hash table with all the variables that we can expand. This
// includes all environment variables being passed to the child. // includes all environment variables being passed to the child.
HashMap *vars; HashMap *vars;
@ -1346,7 +1355,10 @@ static void childProcess(struct Service *service, int width, int height,
pam_handle_t *pam = internalLogin(service, utmp, &environment); pam_handle_t *pam = internalLogin(service, utmp, &environment);
#if defined(HAVE_SECURITY_PAM_APPL_H) #if defined(HAVE_SECURITY_PAM_APPL_H)
if (pam && !geteuid()) { 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(); pid_t pid = fork();
switch (pid) { switch (pid) {
case -1: 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) { static void sigChildHandler(int sig, siginfo_t *info, void *unused) {
(void)sig;
(void)info;
(void)unused;
} }
static void launcherDaemon(int fd) { static void launcherDaemon(int fd) {

View file

@ -1,5 +1,5 @@
// privileges.c -- Manage process privileges // 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 // 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 // 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); getresuid(&ru, &eu, &su);
// Try to switch the user-provided group. // Try to switch the user-provided group.
if ((ru && runAsGroup != rg) || if ((ru && runAsGroup != (int)rg) ||
setresgid(runAsGroup, runAsGroup, runAsGroup)) { setresgid(runAsGroup, runAsGroup, runAsGroup)) {
if (showError) { if (showError) {
fatal("Only privileged users can change their group memberships"); fatal("Only privileged users can change their group memberships");
@ -110,7 +110,7 @@ void lowerPrivileges(void) {
if (runAsUser >= 0) { if (runAsUser >= 0) {
// Try to switch to the user-provided user id. // 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"); fatal("Only privileged users can change their user id");
} }
check(!setresuid(runAsUser, runAsUser, -1)); check(!setresuid(runAsUser, runAsUser, -1));
@ -136,7 +136,7 @@ void dropPrivileges(void) {
if (runAsUser >= 0) { if (runAsUser >= 0) {
// Try to switch to the user-provided user id. // Try to switch to the user-provided user id.
if ((r && runAsUser != r) || if ((r && runAsUser != (int)r) ||
setresuid(runAsUser, runAsUser, runAsUser)) { setresuid(runAsUser, runAsUser, runAsUser)) {
fatal("Only privileged users can change their user id."); fatal("Only privileged users can change their user id.");
} }

View file

@ -1,5 +1,5 @@
// service.c -- Service descriptions // 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 // 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 // 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 destroyServiceHashEntry(void *arg, char *key, char *value) {
(void)arg;
(void)key;
(void)value;
} }
static int enumerateServicesHelper(void *arg, const char *key, char **value) { static int enumerateServicesHelper(void *arg, const char *key, char **value) {
(void)arg;
(void)key;
check(services = realloc(services, check(services = realloc(services,
++numServices * sizeof(struct Service *))); ++numServices * sizeof(struct Service *)));
services[numServices-1] = *(struct Service **)value; services[numServices-1] = *(struct Service **)value;

View file

@ -1,5 +1,5 @@
// service.h -- Service descriptions // 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 // 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 // it under the terms of the GNU General Public License version 2 as
@ -55,8 +55,8 @@ struct Service {
int useHomeDir; int useHomeDir;
int authUser; int authUser;
int useDefaultShell; int useDefaultShell;
int uid; int uid; /* Can be -1 */
int gid; int gid; /* Can be -1 */
const char *user; const char *user;
const char *group; const char *group;
const char *cwd; const char *cwd;

View file

@ -1,5 +1,5 @@
// session.c -- Session management for HTTP/HTTPS connections // 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 // 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 // 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) { static void destroySessionHashEntry(void *arg, char *key, char *value) {
(void)arg;
(void)key;
deleteSession((struct Session *)value); deleteSession((struct Session *)value);
} }
@ -167,7 +170,7 @@ char *newSessionKey(void) {
char *ptr = sessionKey; char *ptr = sessionKey;
int count = 0; int count = 0;
int bits = 0; int bits = 0;
for (int i = 0;;) { for (unsigned i = 0;;) {
bits = (bits << 8) | buf[i]; bits = (bits << 8) | buf[i];
count += 8; count += 8;
drain: drain:

View file

@ -1,5 +1,5 @@
// ShellInABox.js -- Use XMLHttpRequest to provide an AJAX terminal emulator. // 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 // 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 // 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() { ShellInABox.prototype.about = function() {
alert("Shell In A Box version " + "2.10 (revision 200)" + alert("Shell In A Box version " + "2.10 (revision 202)" +
"\nCopyright 2008-2009 by Markus Gutschke\n" + "\nCopyright 2008-2010 by Markus Gutschke\n" +
"For more information check http://shellinabox.com" + "For more information check http://shellinabox.com" +
(typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ? (typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ?
"\n\n" + "\n\n" +

View file

@ -1,5 +1,5 @@
// ShellInABox.js -- Use XMLHttpRequest to provide an AJAX terminal emulator. // 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 // 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 // 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() { ShellInABox.prototype.about = function() {
alert("Shell In A Box version " + VERSION + 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" + "For more information check http://shellinabox.com" +
(typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ? (typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ?
"\n\n" + "\n\n" +

View file

@ -1,6 +1,6 @@
// shellinaboxd.c -- A custom web server that makes command line applications // shellinaboxd.c -- A custom web server that makes command line applications
// available as AJAX web 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 // 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 // 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, static int handleSession(struct ServerConnection *connection, void *arg,
short *events, short revents) { short *events, short revents) {
(void)events;
struct Session *session = (struct Session *)arg; struct Session *session = (struct Session *)arg;
session->connection = connection; session->connection = connection;
int len = MAX_RESPONSE - session->len; 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, static int dataHandler(HttpConnection *http, struct Service *service,
const char *buf, int len, URL *url) { const char *buf, int len, URL *url) {
(void)len;
if (!buf) { if (!buf) {
// Somebody unexpectedly closed our http connection (e.g. because of a // Somebody unexpectedly closed our http connection (e.g. because of a
// timeout). This is the last notification that we will get. // 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 // Remember the beginning of the "[if ...]" statement
ifPtr = ptr; 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, "[else ", 6) &&
!memcmp(ptr + 6, tag, strlen(tag)) && !memcmp(ptr + 6, tag, strlen(tag)) &&
ptr[6 + strlen(tag)] == ']') { ptr[6 + strlen(tag)] == ']') {
// Found an "[else ...]" statement. Remember where it started. // Found an "[else ...]" statement. Remember where it started.
elsePtr = ptr; 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, "[endif ", 7) &&
!memcmp(ptr + 7, tag, strlen(tag)) && !memcmp(ptr + 7, tag, strlen(tag)) &&
ptr[7 + strlen(tag)] == ']') { ptr[7 + strlen(tag)] == ']') {
@ -810,6 +812,7 @@ static void usage(void) {
} }
static void destroyExternalFileHashEntry(void *arg, char *key, char *value) { static void destroyExternalFileHashEntry(void *arg, char *key, char *value) {
(void)arg;
free(key); free(key);
free(value); free(value);
} }
@ -938,7 +941,7 @@ static void parseArgs(int argc, char * const argv[]) {
st.st_size + 2)); st.st_size + 2));
char *newData = strrchr(cssStyleSheet, '\000'); char *newData = strrchr(cssStyleSheet, '\000');
*newData++ = '\n'; *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); fatal("Failed to read style sheet \"%s\"", optarg);
} }
newData[st.st_size]= '\000'; newData[st.st_size]= '\000';
@ -1149,7 +1152,7 @@ static void parseArgs(int argc, char * const argv[]) {
static void removeLimits() { static void removeLimits() {
static int res[] = { RLIMIT_CPU, RLIMIT_DATA, RLIMIT_FSIZE, RLIMIT_NPROC }; 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; struct rlimit rl;
getrlimit(res[i], &rl); getrlimit(res[i], &rl);
if (rl.rlim_max < RLIM_INFINITY) { if (rl.rlim_max < RLIM_INFINITY) {

View file

@ -1,5 +1,5 @@
// usercss.c -- Defines user-selectable CSS options // 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 // 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 // it under the terms of the GNU General Public License version 2 as
@ -60,6 +60,9 @@
static struct HashMap *defines; static struct HashMap *defines;
static void definesDestructor(void *arg, char *key, char *value) { static void definesDestructor(void *arg, char *key, char *value) {
(void)arg;
(void)value;
free(key); free(key);
} }
@ -73,7 +76,7 @@ static void readStylesheet(struct UserCSS *userCSS, const char *filename,
FILE *fp; FILE *fp;
check(fp = fdopen(fd, "r")); check(fp = fdopen(fd, "r"));
check(*style = malloc(st.st_size + 1)); 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'; (*style)[st.st_size] = '\000';
*len = st.st_size; *len = st.st_size;
fclose(fp); fclose(fp);

View file

@ -1,5 +1,5 @@
// VT100.js -- JavaScript based terminal emulator // 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 // 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 // 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() { VT100.prototype.about = function() {
alert("VT100 Terminal Emulator " + "2.10 (revision 200)" + alert("VT100 Terminal Emulator " + "2.10 (revision 202)" +
"\nCopyright 2008-2009 by Markus Gutschke\n" + "\nCopyright 2008-2010 by Markus Gutschke\n" +
"For more information check http://shellinabox.com"); "For more information check http://shellinabox.com");
}; };

View file

@ -1,5 +1,5 @@
// VT100.js -- JavaScript based terminal emulator // 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 // 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 // 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() { VT100.prototype.about = function() {
alert("VT100 Terminal Emulator " + VERSION + 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"); "For more information check http://shellinabox.com");
}; };