summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars-Dominik Braun <lars@6xq.net>2011-09-22 10:39:44 +0200
committerLars-Dominik Braun <lars@6xq.net>2011-11-09 20:10:16 +0100
commit50a5cac2445bc0c199958ac04d5127e4be09fb1e (patch)
tree84e32e498be8f6de52569aa0dda8d64b04da535c
parent1679beb2887f04c322acc06593a36ce80b0129ea (diff)
downloadpianobar-windows-50a5cac2445bc0c199958ac04d5127e4be09fb1e.tar.gz
pianobar-windows-50a5cac2445bc0c199958ac04d5127e4be09fb1e.tar.bz2
pianobar-windows-50a5cac2445bc0c199958ac04d5127e4be09fb1e.zip
waitress: Initial TLS implementation (using gnutls)
-rw-r--r--Makefile16
-rw-r--r--src/libwaitress/waitress.c198
-rw-r--r--src/libwaitress/waitress.h53
-rw-r--r--src/main.c10
4 files changed, 217 insertions, 60 deletions
diff --git a/Makefile b/Makefile
index 2274644..2546ae9 100644
--- a/Makefile
+++ b/Makefile
@@ -85,17 +85,25 @@ else
LIBMAD_LDFLAGS=-lmad
endif
+ifeq (${DISABLE_GNUTLS}, 1)
+ LIBGNUTLS_CFLAGS=
+ LIBGNUTLS_LDFLAGS=
+else
+ LIBGNUTLS_CFLAGS=-DENABLE_TLS
+ LIBGNUTLS_LDFLAGS=-lgnutls
+endif
+
# build pianobar
ifeq (${DYNLINK},1)
pianobar: ${PIANOBAR_OBJ} ${PIANOBAR_HDR} libpiano.so.0
${CC} -o $@ ${PIANOBAR_OBJ} ${LDFLAGS} -lao -lpthread -lm -L. -lpiano \
- ${LIBFAAD_LDFLAGS} ${LIBMAD_LDFLAGS}
+ ${LIBFAAD_LDFLAGS} ${LIBMAD_LDFLAGS} ${LIBGNUTLS_LDFLAGS}
else
pianobar: ${PIANOBAR_OBJ} ${PIANOBAR_HDR} ${LIBPIANO_OBJ} ${LIBWAITRESS_OBJ} \
${LIBWAITRESS_HDR} ${LIBEZXML_OBJ} ${LIBEZXML_HDR}
${CC} ${CFLAGS} ${LDFLAGS} ${PIANOBAR_OBJ} ${LIBPIANO_OBJ} \
${LIBWAITRESS_OBJ} ${LIBEZXML_OBJ} -lao -lpthread -lm \
- ${LIBFAAD_LDFLAGS} ${LIBMAD_LDFLAGS} -o $@
+ ${LIBFAAD_LDFLAGS} ${LIBMAD_LDFLAGS} ${LIBGNUTLS_LDFLAGS} -o $@
endif
# build shared and static libpiano
@@ -112,7 +120,7 @@ libpiano.so.0: ${LIBPIANO_RELOBJ} ${LIBPIANO_HDR} ${LIBWAITRESS_RELOBJ} \
%.o: %.c
${CC} ${CFLAGS} -I ${LIBPIANO_INCLUDE} -I ${LIBWAITRESS_INCLUDE} \
-I ${LIBEZXML_INCLUDE} ${LIBFAAD_CFLAGS} \
- ${LIBMAD_CFLAGS} -c -o $@ $<
+ ${LIBMAD_CFLAGS} ${LIBGNUTLS_CFLAGS} -c -o $@ $<
# create position independent code (for shared libraries)
%.lo: %.c
@@ -131,7 +139,7 @@ debug: CFLAGS=-Wall -pedantic -ggdb
waitress-test: CFLAGS+= -DTEST
waitress-test: ${LIBWAITRESS_OBJ}
- ${CC} ${LDFLAGS} ${LIBWAITRESS_OBJ} -o waitress-test
+ ${CC} ${LDFLAGS} ${LIBWAITRESS_OBJ} ${LIBGNUTLS_LDFLAGS} -o waitress-test
test: waitress-test
./waitress-test
diff --git a/src/libwaitress/waitress.c b/src/libwaitress/waitress.c
index 0c6242d..c7ae583 100644
--- a/src/libwaitress/waitress.c
+++ b/src/libwaitress/waitress.c
@@ -55,7 +55,7 @@ void WaitressInit (WaitressHandle_t *waith) {
assert (waith != NULL);
memset (waith, 0, sizeof (*waith));
- waith->socktimeout = 30000;
+ waith->timeout = 30000;
}
void WaitressFree (WaitressHandle_t *waith) {
@@ -423,63 +423,106 @@ static int WaitressPollLoop (int fd, short events, int timeout) {
}
/* write () wrapper with poll () timeout
- * @param socket fd
+ * @param waitress handle
* @param write buffer
* @param write count bytes
- * @param reuse existing pollfd structure
- * @param timeout (microseconds)
- * @return WAITRESS_RET_OK, WAITRESS_RET_TIMEOUT or WAITRESS_RET_ERR
+ * @return number of written bytes or -1 on error
*/
-static WaitressReturn_t WaitressPollWrite (int sockfd, const char *buf,
- size_t count, int timeout) {
+static ssize_t WaitressPollWrite (WaitressHandle_t *waith,
+ const char *buf, size_t count) {
int pollres = -1;
+ ssize_t retSize;
- assert (sockfd != -1);
+ assert (waith != NULL);
assert (buf != NULL);
- assert (count > 0);
- pollres = WaitressPollLoop (sockfd, POLLOUT, timeout);
+ /* FIXME: simplify logic */
+ pollres = WaitressPollLoop (waith->request.sockfd, POLLOUT,
+ waith->timeout);
if (pollres == 0) {
- return WAITRESS_RET_TIMEOUT;
+ waith->request.readWriteRet = WAITRESS_RET_TIMEOUT;
+ return -1;
} else if (pollres == -1) {
- return WAITRESS_RET_ERR;
+ waith->request.readWriteRet = WAITRESS_RET_ERR;
+ return -1;
}
- if (write (sockfd, buf, count) == -1) {
- return WAITRESS_RET_ERR;
+ if ((retSize = write (waith->request.sockfd, buf, count)) == -1) {
+ waith->request.readWriteRet = WAITRESS_RET_ERR;
+ return -1;
}
- return WAITRESS_RET_OK;
+ waith->request.readWriteRet = WAITRESS_RET_OK;
+ return retSize;
}
+static WaitressReturn_t WaitressOrdinaryWrite (WaitressHandle_t *waith,
+ const char *buf, const size_t size) {
+ WaitressPollWrite (waith, buf, size);
+ return waith->request.readWriteRet;
+}
+
+#ifdef ENABLE_TLS
+static WaitressReturn_t WaitressGnutlsWrite (WaitressHandle_t *waith,
+ const char *buf, const size_t size) {
+ if (gnutls_record_send (waith->request.tlsSession, buf, size) < 0) {
+ return WAITRESS_RET_TLS_WRITE_ERR;
+ }
+ return waith->request.readWriteRet;
+}
+#endif
+
/* read () wrapper with poll () timeout
- * @param socket fd
+ * @param waitress handle
* @param write to this buf, not NULL terminated
* @param buffer size
- * @param reuse existing pollfd struct
- * @param timeout (in microseconds)
- * @param read () return value/written bytes
- * @return WAITRESS_RET_OK, WAITRESS_RET_TIMEOUT, WAITRESS_RET_ERR
+ * @return number of read bytes or -1 on error
*/
-static WaitressReturn_t WaitressPollRead (int sockfd, char *buf, size_t count,
- int timeout, ssize_t *retSize) {
+static ssize_t WaitressPollRead (WaitressHandle_t *waith, char *buf,
+ size_t count) {
int pollres = -1;
+ ssize_t retSize;
- assert (sockfd != -1);
+ assert (waith != NULL);
assert (buf != NULL);
- assert (count > 0);
- assert (retSize != NULL);
- pollres = WaitressPollLoop (sockfd, POLLIN, timeout);
+ /* FIXME: simplify logic */
+ pollres = WaitressPollLoop (waith->request.sockfd, POLLIN, waith->timeout);
if (pollres == 0) {
- return WAITRESS_RET_TIMEOUT;
+ waith->request.readWriteRet = WAITRESS_RET_TIMEOUT;
+ return -1;
} else if (pollres == -1) {
- return WAITRESS_RET_ERR;
+ waith->request.readWriteRet = WAITRESS_RET_ERR;
+ return -1;
}
- if ((*retSize = read (sockfd, buf, count)) == -1) {
- return WAITRESS_RET_READ_ERR;
+ if ((retSize = read (waith->request.sockfd, buf, count)) == -1) {
+ waith->request.readWriteRet = WAITRESS_RET_READ_ERR;
+ return -1;
}
- return WAITRESS_RET_OK;
+ waith->request.readWriteRet = WAITRESS_RET_OK;
+ return retSize;
+}
+
+static WaitressReturn_t WaitressOrdinaryRead (WaitressHandle_t *waith,
+ char *buf, const size_t size, size_t *retSize) {
+ const ssize_t ret = WaitressPollRead (waith, buf, size);
+ if (ret != -1) {
+ *retSize = ret;
+ }
+ return waith->request.readWriteRet;
}
+#ifdef ENABLE_TLS
+static WaitressReturn_t WaitressGnutlsRead (WaitressHandle_t *waith,
+ char *buf, const size_t size, size_t *retSize) {
+ ssize_t ret = gnutls_record_recv (waith->request.tlsSession, buf, size);
+ if (ret < 0) {
+ return WAITRESS_RET_TLS_READ_ERR;
+ } else {
+ *retSize = ret;
+ }
+ return waith->request.readWriteRet;
+}
+#endif
+
/* send basic http authorization
* @param waitress handle
* @param url containing user/password
@@ -513,7 +556,7 @@ static bool WaitressFormatAuthorization (WaitressHandle_t *waith,
static const char *WaitressDefaultPort (const WaitressUrl_t * const url) {
assert (url != NULL);
- return url->port == NULL ? "80" : url->port;
+ return url->port == NULL ? (url->tls ? "443" : "80") : url->port;
}
/* get line from string
@@ -686,7 +729,7 @@ static WaitressReturn_t WaitressConnect (WaitressHandle_t *waith) {
connect (waith->request.sockfd, res->ai_addr, res->ai_addrlen);
pollres = WaitressPollLoop (waith->request.sockfd, POLLOUT,
- waith->socktimeout);
+ waith->timeout);
freeaddrinfo (res);
if (pollres == 0) {
return WAITRESS_RET_TIMEOUT;
@@ -701,6 +744,14 @@ static WaitressReturn_t WaitressConnect (WaitressHandle_t *waith) {
return WAITRESS_RET_CONNECT_REFUSED;
}
+#ifdef ENABLE_TLS
+ if (waith->url.tls) {
+ if (gnutls_handshake (waith->request.tlsSession) != GNUTLS_E_SUCCESS) {
+ return WAITRESS_RET_TLS_HANDSHAKE_ERR;
+ }
+ }
+#endif
+
return WAITRESS_RET_OK;
}
@@ -708,8 +759,7 @@ static WaitressReturn_t WaitressConnect (WaitressHandle_t *waith) {
*/
static WaitressReturn_t WaitressSendRequest (WaitressHandle_t *waith) {
#define WRITE_RET(buf, count) \
- if ((wRet = WaitressPollWrite (waith->request.sockfd, buf, count, \
- waith->socktimeout)) != WAITRESS_RET_OK) { \
+ if ((wRet = waith->request.write (waith, buf, count)) != WAITRESS_RET_OK) { \
return wRet; \
}
@@ -782,8 +832,8 @@ static WaitressReturn_t WaitressSendRequest (WaitressHandle_t *waith) {
*/
static WaitressReturn_t WaitressReceiveResponse (WaitressHandle_t *waith) {
#define READ_RET(buf, count, size) \
- if ((wRet = WaitressPollRead (waith->request.sockfd, buf, count, \
- waith->socktimeout, size)) != WAITRESS_RET_OK) { \
+ if ((wRet = waith->request.read (waith, buf, count, size)) != \
+ WAITRESS_RET_OK) { \
return wRet; \
}
@@ -907,6 +957,42 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) {
/* initialize */
memset (&waith->request, 0, sizeof (waith->request));
waith->request.dataHandler = WaitressHandleIdentity;
+ waith->request.read = WaitressOrdinaryRead;
+ waith->request.write = WaitressOrdinaryWrite;
+
+#ifdef ENABLE_TLS
+ if (waith->url.tls) {
+ waith->request.read = WaitressGnutlsRead;
+ waith->request.write = WaitressGnutlsWrite;
+ /* FIXME: move creds to waitressinit */
+ gnutls_certificate_allocate_credentials (&waith->request.tlsCred);
+ gnutls_certificate_set_x509_trust_file (waith->request.tlsCred,
+ "/etc/ssl/certs/ca-certificates.crt", GNUTLS_X509_FMT_PEM);
+ gnutls_init (&waith->request.tlsSession, GNUTLS_CLIENT);
+ const char *err;
+ if (gnutls_priority_set_direct (waith->request.tlsSession,
+ "PERFORMANCE", &err) != GNUTLS_E_SUCCESS) {
+ return WAITRESS_RET_ERR;
+ }
+ if (gnutls_credentials_set (waith->request.tlsSession,
+ GNUTLS_CRD_CERTIFICATE,
+ waith->request.tlsCred) != GNUTLS_E_SUCCESS) {
+ return WAITRESS_RET_ERR;
+ }
+
+ /* set up custom read/write functions */
+ gnutls_transport_set_ptr (waith->request.tlsSession,
+ (gnutls_transport_ptr_t) waith);
+ gnutls_transport_set_pull_function (waith->request.tlsSession,
+ WaitressPollRead);
+ gnutls_transport_set_push_function (waith->request.tlsSession,
+ WaitressPollWrite);
+ }
+#else
+ if (waith->url.tls) {
+ return WAITRESS_RET_TLS_DISABLED;
+ }
+#endif
/* request */
if ((wRet = WaitressConnect (waith)) == WAITRESS_RET_OK) {
@@ -921,6 +1007,13 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) {
}
/* cleanup */
+#ifdef ENABLE_TLS
+ if (waith->url.tls) {
+ gnutls_bye (waith->request.tlsSession, GNUTLS_SHUT_RDWR);
+ gnutls_deinit (waith->request.tlsSession);
+ gnutls_certificate_free_credentials (waith->request.tlsCred);
+ }
+#endif
close (waith->request.sockfd);
if (wRet == WAITRESS_RET_OK &&
@@ -988,6 +1081,22 @@ const char *WaitressErrorToStr (WaitressReturn_t wRet) {
return "Invalid encoded data.";
break;
+ case WAITRESS_RET_TLS_DISABLED:
+ return "TLS has been disabled.";
+ break;
+
+ case WAITRESS_RET_TLS_WRITE_ERR:
+ return "TLS write failed.";
+ break;
+
+ case WAITRESS_RET_TLS_READ_ERR:
+ return "TLS read failed.";
+ break;
+
+ case WAITRESS_RET_TLS_HANDSHAKE_ERR:
+ return "TLS handshake failed.";
+ break;
+
default:
return "No error message available.";
break;
@@ -1110,6 +1219,21 @@ int main () {
compareStr (WaitressBase64Encode ("The quick brown fox jumped over the lazy do"),
"VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkbw==");
+#ifdef ENABLE_TLS
+ gnutls_global_init ();
+#endif
+ WaitressHandle_t waith;
+ char *buf;
+ WaitressInit (&waith);
+ WaitressSetUrl (&waith, "http://6xq.net:80/");
+ printf ("ret: %s\n", WaitressErrorToStr (WaitressFetchBuf (&waith, &buf)));
+ printf ("%s\n", buf);
+ free (buf);
+ WaitressFree (&waith);
+#ifdef ENABLE_TLS
+ gnutls_global_deinit ();
+#endif
+
return EXIT_SUCCESS;
}
#endif /* TEST */
diff --git a/src/libwaitress/waitress.h b/src/libwaitress/waitress.h
index cbf17c1..9523ede 100644
--- a/src/libwaitress/waitress.h
+++ b/src/libwaitress/waitress.h
@@ -25,7 +25,11 @@ THE SOFTWARE.
#define _WAITRESS_H
#include <stdlib.h>
+#include <unistd.h>
#include <stdbool.h>
+#ifdef ENABLE_TLS
+#include <gnutls/gnutls.h>
+#endif
#define WAITRESS_BUFFER_SIZE 10*1024
@@ -48,6 +52,7 @@ typedef enum {
typedef struct {
char *url; /* splitted url, unusable */
+ bool tls;
const char *user;
const char *password;
const char *host;
@@ -55,6 +60,27 @@ typedef struct {
const char *path; /* without leading '/' */
} WaitressUrl_t;
+typedef enum {
+ WAITRESS_RET_ERR = 0,
+ WAITRESS_RET_OK,
+ WAITRESS_RET_STATUS_UNKNOWN,
+ WAITRESS_RET_NOTFOUND,
+ WAITRESS_RET_FORBIDDEN,
+ WAITRESS_RET_CONNECT_REFUSED,
+ WAITRESS_RET_SOCK_ERR,
+ WAITRESS_RET_GETADDR_ERR,
+ WAITRESS_RET_CB_ABORT,
+ WAITRESS_RET_PARTIAL_FILE,
+ WAITRESS_RET_TIMEOUT,
+ WAITRESS_RET_READ_ERR,
+ WAITRESS_RET_CONNECTION_CLOSED,
+ WAITRESS_RET_DECODING_ERR,
+ WAITRESS_RET_TLS_DISABLED,
+ WAITRESS_RET_TLS_WRITE_ERR,
+ WAITRESS_RET_TLS_READ_ERR,
+ WAITRESS_RET_TLS_HANDSHAKE_ERR,
+} WaitressReturn_t;
+
/* reusable handle
*/
typedef struct {
@@ -66,34 +92,25 @@ typedef struct {
/* extra data handed over to callback function */
void *data;
WaitressCbReturn_t (*callback) (void *, size_t, void *);
- int socktimeout;
+ int timeout;
/* per-request data */
struct {
size_t contentLength, contentReceived, chunkSize;
int sockfd;
char *buf;
+#ifdef ENABLE_TLS
+ gnutls_session_t tlsSession;
+ gnutls_certificate_credentials_t tlsCred;
+#endif
/* first argument is WaitressHandle_t, but that's not defined here */
WaitressHandlerReturn_t (*dataHandler) (void *, char *, const size_t);
+ ssize_t (*read) (void *, char *, const size_t, ssize_t *);
+ ssize_t (*write) (void *, const char *, const size_t);
+ /* temporary return value storage */
+ WaitressReturn_t readWriteRet;
} request;
} WaitressHandle_t;
-typedef enum {
- WAITRESS_RET_ERR = 0,
- WAITRESS_RET_OK,
- WAITRESS_RET_STATUS_UNKNOWN,
- WAITRESS_RET_NOTFOUND,
- WAITRESS_RET_FORBIDDEN,
- WAITRESS_RET_CONNECT_REFUSED,
- WAITRESS_RET_SOCK_ERR,
- WAITRESS_RET_GETADDR_ERR,
- WAITRESS_RET_CB_ABORT,
- WAITRESS_RET_PARTIAL_FILE,
- WAITRESS_RET_TIMEOUT,
- WAITRESS_RET_READ_ERR,
- WAITRESS_RET_CONNECTION_CLOSED,
- WAITRESS_RET_DECODING_ERR,
-} WaitressReturn_t;
-
void WaitressInit (WaitressHandle_t *);
void WaitressFree (WaitressHandle_t *);
bool WaitressSetProxy (WaitressHandle_t *, const char *);
diff --git a/src/main.c b/src/main.c
index ed80753..bcc7997 100644
--- a/src/main.c
+++ b/src/main.c
@@ -338,11 +338,16 @@ int main (int argc, char **argv) {
/* init some things */
ao_initialize ();
+#ifdef ENABLE_TLS
+ gnutls_global_init ();
+#endif
PianoInit (&app.ph);
WaitressInit (&app.waith);
app.waith.url.host = strdup (PIANO_RPC_HOST);
- app.waith.url.port = strdup (PIANO_RPC_PORT);
+#ifdef ENABLE_TLS
+ app.waith.url.tls = true;
+#endif
BarSettingsInit (&app.settings);
BarSettingsRead (&app.settings);
@@ -384,6 +389,9 @@ int main (int argc, char **argv) {
PianoDestroyPlaylist (app.songHistory);
PianoDestroyPlaylist (app.playlist);
ao_shutdown();
+#ifdef ENABLE_TLS
+ gnutls_global_deinit ();
+#endif
BarSettingsDestroy (&app.settings);
/* restore terminal attributes, zsh doesn't need this, bash does... */