unsigned sa_tport_tcp : 1; /**< Transports support TCP. */
unsigned sa_tport_sctp : 1; /**< Transports support SCTP. */
unsigned sa_tport_tls : 1; /**< Transports support TLS. */
+ unsigned sa_tport_ws : 1; /**< Transports support WS. */
+ unsigned sa_tport_wss : 1; /**< Transports support WSS. */
unsigned sa_use_naptr : 1; /**< Use NAPTR lookup */
unsigned sa_use_srv : 1; /**< Use SRV lookup */
char prefix[14]; /**< Prefix for SRV domains */
char service[10]; /**< NAPTR service */
}
-#define SIPDNS_TRANSPORTS (4)
+#define SIPDNS_TRANSPORTS (6)
const sipdns_tports[SIPDNS_TRANSPORTS] = {
{ "udp", "5060", "_sip._udp.", "SIP+D2U" },
{ "tcp", "5060", "_sip._tcp.", "SIP+D2T" },
- { "sctp", "5060", "_sip._sctp.", "SIP+D2S" },
- { "tls", "5061", "_sips._tcp.", "SIPS+D2T" },
+ { "sctp", "5060", "_sip._sctp.", "SIP+D2S" },
+ { "tls", "5061", "_sips._tcp.", "SIPS+D2T" },
+ { "ws", "80", "_sips._ws.", "SIP+D2W" },
+ { "wss", "443", "_sips._wss.", "SIPS+D2W" },
};
static char const * const tports_sip[] =
{
- "udp", "tcp", "sctp", NULL
+ "udp", "tcp", "sctp", "ws", NULL
};
static char const * const tports_sips[] =
{
- "tls", NULL
+ "tls", "ws", NULL
};
static tport_stack_class_t nta_agent_class[1] =
if (url->url_params) {
if (url_param(url->url_params, "transport", tp, sizeof(tp)) > 0) {
if (strchr(tp, ',')) {
- int i; char *t, *tps[9];
+ int i; char *t, *tps[9] = {0};
/* Split tp into transports */
for (i = 0, t = tp; t && i < 8; i++) {
self->sa_tport_tcp = 0;
self->sa_tport_sctp = 0;
self->sa_tport_tls = 0;
+ self->sa_tport_ws = 0;
+ self->sa_tport_wss = 0;
/* Set via fields for the tports */
for (tp = primaries; tp; tp = tport_next(tp)) {
self->sa_tport_tcp = 1;
else if (su_casematch(tpn->tpn_proto, "sctp"))
self->sa_tport_sctp = 1;
+ else if (su_casematch(tpn->tpn_proto, "ws"))
+ self->sa_tport_ws = 1;
+ else if (su_casematch(tpn->tpn_proto, "wss"))
+ self->sa_tport_wss = 1;
if (tport_has_tls(tp)) self->sa_tport_tls = 1;
tpn->tpn_ident = NULL;
- if (tpn->tpn_proto)
+ if (tpn->tpn_proto) {
+ if (su_casematch(url->url_scheme, "sips") && su_casematch(tpn->tpn_proto, "ws")) {
+ tpn->tpn_proto = "wss";
+ }
return 1;
+ }
if (su_casematch(url->url_scheme, "sips"))
tpn->tpn_proto = "tls";
USE_STUN_SRC = $(STUN_SRC)
endif
-HTTP_SRC = tport_type_connect.c
+HTTP_SRC = tport_type_connect.c tport_type_ws.c ws.c
if HAVE_NTH
USE_HTTP_SRC = $(HTTP_SRC)
endif
/** Test if transport is stream. */
int tport_is_stream(tport_t const *self)
{
- return self && self->tp_addrinfo->ai_socktype == SOCK_STREAM;
+ return self && !self->tp_pre_framed && self->tp_addrinfo->ai_socktype == SOCK_STREAM;
}
/** Test if transport is dgram. */
extern tport_vtable_t const tport_udp_vtable;
extern tport_vtable_t const tport_tcp_vtable;
extern tport_vtable_t const tport_tls_vtable;
+extern tport_vtable_t const tport_ws_vtable;
extern tport_vtable_t const tport_sctp_vtable;
extern tport_vtable_t const tport_udp_client_vtable;
extern tport_vtable_t const tport_tcp_client_vtable;
extern tport_vtable_t const tport_sctp_client_vtable;
+extern tport_vtable_t const tport_ws_client_vtable;
extern tport_vtable_t const tport_tls_client_vtable;
extern tport_vtable_t const tport_http_connect_vtable;
extern tport_vtable_t const tport_threadpool_vtable;
{
#if HAVE_SOFIA_NTH
&tport_http_connect_vtable,
+ &tport_ws_client_vtable,
+ &tport_ws_vtable,
#endif
#if HAVE_TLS
&tport_tls_client_vtable,
proto = "tcp";
#endif
+#if HAVE_SOFIA_NTH
+ if (su_casematch(proto, "ws"))
+ proto = "tcp";
+ if (su_casematch(proto, "wss"))
+ proto = "tcp";
+#endif
+
#if HAVE_SCTP
if (su_casematch(proto, "sctp")) {
hints->ai_protocol = IPPROTO_SCTP;
}
if (again >= 0)
- tport_parse(self, !again, self->tp_rtime);
+ tport_parse(self, self->tp_pre_framed ? 1 : !again, self->tp_rtime);
}
while (again > 1);
unsigned tp_trunc:1;
unsigned tp_is_connected:1; /**< Connection is established */
unsigned tp_verified:1; /**< Certificate Chain was verified */
+ unsigned tp_pre_framed:1; /** Data is pre-framed **/
unsigned:0;
tport_t *tp_left, *tp_right, *tp_dad; /**< Links in tport tree */
int tport_next_keepalive(tport_t *self, su_time_t *, char const **);
void tport_keepalive_timer(tport_t *self, su_time_t now);
+extern tport_vtable_t const tport_ws_vtable;
+extern tport_vtable_t const tport_ws_client_vtable;
+extern tport_vtable_t const tport_wss_vtable;
+extern tport_vtable_t const tport_wss_client_vtable;
extern tport_vtable_t const tport_sctp_vtable;
extern tport_vtable_t const tport_sctp_client_vtable;
extern tport_vtable_t const tport_tls_vtable;
if(strcmp(self->tp_name->tpn_proto, "tcp") == 0) hep_header.hp_p = IPPROTO_TCP;
else if(strcmp(self->tp_name->tpn_proto, "tls") == 0) hep_header.hp_p = IPPROTO_IDP; /* FAKE*/
else if(strcmp(self->tp_name->tpn_proto, "sctp") == 0) hep_header.hp_p = IPPROTO_SCTP;
+ else if(strcmp(self->tp_name->tpn_proto, "ws") == 0) hep_header.hp_p = IPPROTO_TCP;
+ else if(strcmp(self->tp_name->tpn_proto, "wss") == 0) hep_header.hp_p = IPPROTO_TCP;
else hep_header.hp_p = IPPROTO_UDP; /* DEFAULT UDP */
/* Check destination */
--- /dev/null
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2006 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/**@CFILE tport_type_ws.c WS Transport
+ *
+ * See tport.docs for more detailed description of tport interface.
+ *
+ * @author Pekka Pessi <Pekka.Pessi@nokia.com>
+ * @author Martti Mela <Martti.Mela@nokia.com>
+ *
+ * @date Created: Fri Mar 24 08:45:49 EET 2006 ppessi
+ */
+
+#include "config.h"
+
+#include "tport_internal.h"
+#include "tport_ws.h"
+
+#if HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+#ifndef SOL_TCP
+#define SOL_TCP IPPROTO_TCP
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+
+#if HAVE_FUNC
+#elif HAVE_FUNCTION
+#define __func__ __FUNCTION__
+#else
+static char const __func__[] = "tport_type_ws";
+#endif
+
+/* ---------------------------------------------------------------------- */
+/* WS */
+
+#include <sofia-sip/http.h>
+#include <sofia-sip/http_header.h>
+
+tport_vtable_t const tport_ws_vtable =
+{
+ /* vtp_name */ "ws",
+ /* vtp_public */ tport_type_local,
+ /* vtp_pri_size */ sizeof (tport_primary_t),
+ /* vtp_init_primary */ tport_ws_init_primary,
+ /* vtp_deinit_primary */ NULL,
+ /* vtp_wakeup_pri */ tport_accept,
+ /* vtp_connect */ NULL,
+ /* vtp_secondary_size */ sizeof (tport_ws_t),
+ /* vtp_init_secondary */ tport_ws_init_secondary,
+ /* vtp_deinit_secondary */ NULL,
+ /* vtp_shutdown */ NULL,
+ /* vtp_set_events */ NULL,
+ /* vtp_wakeup */ NULL,
+ /* vtp_recv */ tport_recv_stream_ws,
+ /* vtp_send */ tport_send_stream_ws,
+ /* vtp_deliver */ NULL,
+ /* vtp_prepare */ NULL,
+ /* vtp_keepalive */ NULL,
+ /* vtp_stun_response */ NULL,
+ /* vtp_next_secondary_timer*/ tport_ws_next_timer,
+ /* vtp_secondary_timer */ tport_ws_timer,
+};
+
+tport_vtable_t const tport_ws_client_vtable =
+{
+ /* vtp_name */ "ws",
+ /* vtp_public */ tport_type_client,
+ /* vtp_pri_size */ sizeof (tport_primary_t),
+ /* vtp_init_primary */ tport_ws_init_client,
+ /* vtp_deinit_primary */ NULL,
+ /* vtp_wakeup_pri */ NULL,
+ /* vtp_connect */ NULL,
+ /* vtp_secondary_size */ sizeof (tport_ws_t),
+ /* vtp_init_secondary */ tport_ws_init_secondary,
+ /* vtp_deinit_secondary */ NULL,
+ /* vtp_shutdown */ NULL,
+ /* vtp_set_events */ NULL,
+ /* vtp_wakeup */ NULL,
+ /* vtp_recv */ tport_recv_stream_ws,
+ /* vtp_send */ tport_send_stream_ws,
+ /* vtp_deliver */ NULL,
+ /* vtp_prepare */ NULL,
+ /* vtp_keepalive */ NULL,
+ /* vtp_stun_response */ NULL,
+ /* vtp_next_secondary_timer*/ tport_ws_next_timer,
+ /* vtp_secondary_timer */ tport_ws_timer,
+};
+
+static int tport_ws_setsndbuf(int socket, int atleast);
+
+
+/** Receive from stream.
+ *
+ * @retval -1 error
+ * @retval 0 end-of-stream
+ * @retval 1 normal receive
+ * @retval 2 incomplete recv, recv again
+ *
+ */
+int tport_recv_stream_ws(tport_t *self)
+{
+ msg_t *msg;
+ ssize_t n, N, veclen, i, m;
+ int err;
+ msg_iovec_t iovec[msg_n_fragments] = {{ 0 }};
+ tport_ws_t *wstp = (tport_ws_t *)self;
+ wsh_t *ws = wstp->ws;
+ uint8_t *data;
+ ws_opcode_t oc;
+
+ if ( !wstp->ws_initialized ) {
+ ws_init(ws, self->tp_socket, 65336, wstp->ws_secure);
+ wstp->ws_initialized = 1;
+ self->tp_pre_framed = 1;
+ return 1;
+ }
+
+ N = ws_read_frame(ws, &oc, &data);
+
+ if (N == -1000) {
+ if (self->tp_msg)
+ msg_recv_commit(self->tp_msg, 0, 1);
+ return 0; /* End of stream */
+ }
+ if (N < 0) {
+ err = su_errno();
+ SU_DEBUG_1(("%s(%p): su_getmsgsize(): %s (%d)\n", __func__, (void *)self,
+ su_strerror(err), err));
+ return -1;
+ }
+
+ veclen = tport_recv_iovec(self, &self->tp_msg, iovec, N, 0);
+ if (veclen < 0)
+ return -1;
+
+ msg = self->tp_msg;
+
+ msg_set_address(msg, self->tp_addr, self->tp_addrlen);
+
+ for (i = 0, n = 0; i < veclen; i++) {
+ m = iovec[i].mv_len; assert(N >= n + m);
+ memcpy(iovec[i].mv_base, data + n, m);
+ n += m;
+ }
+
+ assert(N == n);
+
+ /* Write the received data to the message dump file */
+ if (self->tp_master->mr_dump_file)
+ tport_dump_iovec(self, msg, n, iovec, veclen, "recv", "from");
+
+ /* Mark buffer as used */
+ msg_recv_commit(msg, N, 0);
+
+ return 1;
+}
+
+/** Send to stream */
+ssize_t tport_send_stream_ws(tport_t const *self, msg_t *msg,
+ msg_iovec_t iov[],
+ size_t iovlen)
+{
+ size_t i, j, n, m, size = 0;
+ ssize_t nerror;
+ tport_ws_t *wstp = (tport_ws_t *)self;
+ wsh_t *ws = wstp->ws;
+ char xbuf[65536] = "";
+ int blen = 0;
+
+
+ enum { WSBUFSIZE = 2048 };
+
+
+ for (i = 0; i < iovlen; i = j) {
+ char *buf = wstp->wstp_buffer;
+ unsigned wsbufsize = WSBUFSIZE;
+
+ if (i + 1 == iovlen) {
+ buf = NULL; /* Don't bother copying single chunk */
+ }
+
+ if (buf &&
+ (char *)iov[i].siv_base - buf < WSBUFSIZE &&
+ (char *)iov[i].siv_base - buf >= 0) {
+ wsbufsize = buf + WSBUFSIZE - (char *)iov[i].siv_base;
+ assert(wsbufsize <= WSBUFSIZE);
+ }
+
+ for (j = i, m = 0; buf && j < iovlen; j++) {
+ if (m + iov[j].siv_len > wsbufsize) {
+ break;
+ }
+ if (buf + m != iov[j].siv_base) {
+ memcpy(buf + m, iov[j].siv_base, iov[j].siv_len);
+ }
+ m += iov[j].siv_len; iov[j].siv_len = 0;
+ }
+
+ if (j == i) {
+ buf = iov[i].siv_base, m = iov[i].siv_len, j++;
+ } else {
+ iov[j].siv_base = buf, iov[j].siv_len = m;
+ }
+
+ //* hacked to push to buffer
+ if (blen + m < sizeof(xbuf)) {
+ memcpy(xbuf+blen, buf, m);
+ nerror = m;
+ blen += m;
+ } else {
+ nerror = -1;
+ }
+ //*/
+ //nerror = ws_write_frame(ws, WSOC_TEXT, buf, m);
+
+ SU_DEBUG_9(("tport_ws_writevec: vec %p %p %lu ("MOD_ZD")\n",
+ (void *)ws, (void *)iov[i].siv_base, (LU)iov[i].siv_len,
+ nerror));
+
+ if (nerror == -1) {
+ int err = su_errno();
+ if (su_is_blocking(err))
+ break;
+ SU_DEBUG_3(("ws_write: %s\n", strerror(err)));
+ return -1;
+ }
+
+ n = (size_t)nerror;
+ size += n;
+
+ /* Return if the write buffer is full for now */
+ if (n != m)
+ break;
+ }
+
+ //* hacked ....
+ if (size) {
+ size = ws_write_frame(ws, WSOC_TEXT, xbuf, blen);
+ }
+ //*/
+
+ return size;
+}
+
+
+int tport_ws_init_primary(tport_primary_t *pri,
+ tp_name_t tpn[1],
+ su_addrinfo_t *ai,
+ tagi_t const *tags,
+ char const **return_culprit)
+{
+ int socket;
+
+ socket = su_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+
+ if (socket == INVALID_SOCKET)
+ return *return_culprit = "socket", -1;
+
+ tport_ws_setsndbuf(socket, 64 * 1024);
+
+ return tport_stream_init_primary(pri, socket, tpn, ai, tags, return_culprit);
+}
+
+int tport_ws_init_client(tport_primary_t *pri,
+ tp_name_t tpn[1],
+ su_addrinfo_t *ai,
+ tagi_t const *tags,
+ char const **return_culprit)
+{
+ pri->pri_primary->tp_conn_orient = 1;
+
+ return 0;
+}
+
+int tport_ws_init_secondary(tport_t *self, int socket, int accepted,
+ char const **return_reason)
+{
+ int one = 1;
+
+ self->tp_has_connection = 1;
+
+ if (setsockopt(socket, SOL_TCP, TCP_NODELAY, (void *)&one, sizeof one) == -1)
+ return *return_reason = "TCP_NODELAY", -1;
+
+ if (!accepted)
+ tport_ws_setsndbuf(socket, 64 * 1024);
+
+ return 0;
+}
+
+static int tport_ws_setsndbuf(int socket, int atleast)
+{
+#if SU_HAVE_WINSOCK2
+ /* Set send buffer size to something reasonable on windows */
+ int size = 0;
+ socklen_t sizelen = sizeof size;
+
+ if (getsockopt(socket, SOL_SOCKET, SO_SNDBUF, (void *)&size, &sizelen) < 0)
+ return -1;
+
+ if (sizelen != sizeof size)
+ return su_seterrno(EINVAL);
+
+ if (size >= atleast)
+ return 0; /* OK */
+
+ return setsockopt(socket, SOL_SOCKET, SO_SNDBUF,
+ (void *)&atleast, sizeof atleast);
+#else
+ return 0;
+#endif
+}
+
+
+/** Send PING */
+int tport_ws_ping(tport_t *self, su_time_t now)
+{
+ ssize_t n;
+ char *why = "";
+
+ if (tport_has_queued(self))
+ return 0;
+
+ n = send(self->tp_socket, "\r\n\r\n", 4, 0);
+
+ if (n > 0)
+ self->tp_ktime = now;
+
+ if (n == 4) {
+ if (self->tp_ptime.tv_sec == 0)
+ self->tp_ptime = now;
+ }
+ else if (n == -1) {
+ int error = su_errno();
+
+ why = " failed";
+
+ if (!su_is_blocking(error))
+ tport_error_report(self, error, NULL);
+ else
+ why = " blocking";
+
+ return -1;
+ }
+
+ SU_DEBUG_7(("%s(%p): %s to " TPN_FORMAT "%s\n",
+ __func__, (void *)self,
+ "sending PING", TPN_ARGS(self->tp_name), why));
+
+ return n == -1 ? -1 : 0;
+}
+
+/** Send pong */
+int tport_ws_pong(tport_t *self)
+{
+ self->tp_ping = 0;
+
+ if (tport_has_queued(self) || !self->tp_params->tpp_pong2ping)
+ return 0;
+
+ SU_DEBUG_7(("%s(%p): %s to " TPN_FORMAT "%s\n",
+ __func__, (void *)self,
+ "sending PONG", TPN_ARGS(self->tp_name), ""));
+
+ return send(self->tp_socket, "\r\n", 2, 0);
+}
+
+/** Calculate next timer for WS. */
+int tport_ws_next_timer(tport_t *self,
+ su_time_t *return_target,
+ char const **return_why)
+{
+ return
+ tport_next_recv_timeout(self, return_target, return_why) |
+ tport_next_keepalive(self, return_target, return_why);
+}
+
+/** WS timer. */
+void tport_ws_timer(tport_t *self, su_time_t now)
+{
+ tport_recv_timeout_timer(self, now);
+ tport_keepalive_timer(self, now);
+ tport_base_timer(self, now);
+}
--- /dev/null
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef TPORT_WS_H
+/** Defined when <tport_ws.h> has been included. */
+#define TPORT_WS_H
+/**@internal
+ * @file tport_ws.h
+ * @brief Internal WS interface
+ *
+ * @author Mike Jerris <mike@jerris.com>
+ *
+ * Copyright 2013 Michael Jerris. All rights reserved.
+ *
+ */
+
+#ifndef SU_TYPES_H
+#include <sofia-sip/su_types.h>
+#endif
+
+#include "tport_internal.h"
+#include "ws.h"
+
+SOFIA_BEGIN_DECLS
+
+typedef enum {
+ TPORT_WS_OPCODE_CONTINUATION = 0x0,
+ TPORT_WS_OPCODE_TEXT = 0x1,
+ TPORT_WS_OPCODE_BINARY = 0x2,
+ TPORT_WS_OPCODE_CLOSE = 0x8,
+ TPORT_WS_OPCODE_PING = 0x9,
+ TPORT_WS_OPCODE_PONG = 0xA
+} tport_ws_opcode_t;
+
+typedef struct tport_ws_s {
+ tport_t wstp_tp[1];
+ wsh_t ws[1];
+ char *wstp_buffer;
+ unsigned ws_initialized:1;
+ unsigned ws_secure:1;
+ unsigned:0;
+} tport_ws_t;
+
+int tport_recv_stream_ws(tport_t *self);
+ssize_t tport_send_stream_ws(tport_t const *self, msg_t *msg,
+ msg_iovec_t iov[], size_t iovused);
+
+int tport_ws_ping(tport_t *self, su_time_t now);
+int tport_ws_pong(tport_t *self);
+
+int tport_ws_init_primary(tport_primary_t *,
+ tp_name_t tpn[1],
+ su_addrinfo_t *, tagi_t const *,
+ char const **return_culprit);
+int tport_ws_init_client(tport_primary_t *,
+ tp_name_t tpn[1],
+ su_addrinfo_t *, tagi_t const *,
+ char const **return_culprit);
+int tport_ws_init_secondary(tport_t *self, int socket, int accepted,
+ char const **return_reason);
+
+int tport_ws_next_timer(tport_t *self, su_time_t *, char const **);
+void tport_ws_timer(tport_t *self, su_time_t);
+
+SOFIA_END_DECLS
+
+#endif
--- /dev/null
+#include "ws.h"
+
+#define SHA1_HASH_SIZE 20
+struct globals_s globals;
+
+#ifndef PTHREAD
+void ssl_init() {}
+#else
+static unsigned long pthreads_thread_id(void);
+static void pthreads_locking_callback(int mode, int type, const char *file, int line);
+
+static pthread_mutex_t *lock_cs;
+static long *lock_count;
+
+
+
+static void thread_setup(void)
+{
+ int i;
+
+ lock_cs = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
+ lock_count = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));
+
+ for (i = 0; i < CRYPTO_num_locks(); i++) {
+ lock_count[i] = 0;
+ pthread_mutex_init(&(lock_cs[i]), NULL);
+ }
+
+ CRYPTO_set_id_callback(pthreads_thread_id);
+ CRYPTO_set_locking_callback(pthreads_locking_callback);
+}
+
+static void thread_cleanup(void)
+{
+ int i;
+
+ CRYPTO_set_locking_callback(NULL);
+
+ for (i=0; i<CRYPTO_num_locks(); i++) {
+ pthread_mutex_destroy(&(lock_cs[i]));
+ }
+ OPENSSL_free(lock_cs);
+ OPENSSL_free(lock_count);
+
+}
+
+static void pthreads_locking_callback(int mode, int type, const char *file, int line)
+{
+
+ if (mode & CRYPTO_LOCK) {
+ pthread_mutex_lock(&(lock_cs[type]));
+ lock_count[type]++;
+ } else {
+ pthread_mutex_unlock(&(lock_cs[type]));
+ }
+}
+
+
+
+static unsigned long pthreads_thread_id(void)
+{
+ return (unsigned long) pthread_self();
+}
+
+
+void init_ssl(void) {
+ SSL_library_init();
+ // OpenSSL_add_all_algorithms(); /* load & register cryptos */
+ // SSL_load_error_strings(); /* load all error messages */
+ globals.ssl_method = SSLv23_server_method(); /* create server instance */
+ globals.ssl_ctx = SSL_CTX_new(globals.ssl_method); /* create context */
+ assert(globals.ssl_ctx);
+
+ /* set the local certificate from CertFile */
+ SSL_CTX_use_certificate_file(globals.ssl_ctx, globals.cert, SSL_FILETYPE_PEM);
+ /* set the private key from KeyFile */
+ SSL_CTX_use_PrivateKey_file(globals.ssl_ctx, globals.key, SSL_FILETYPE_PEM);
+ /* verify private key */
+ if ( !SSL_CTX_check_private_key(globals.ssl_ctx) ) {
+ abort();
+ }
+
+ thread_setup();
+
+}
+
+
+void deinit_ssl(void) {
+ thread_cleanup();
+}
+
+#endif
+
+static const char c64[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+
+static int cheezy_get_var(char *data, char *name, char *buf, size_t buflen)
+{
+ char *p=data;
+
+ /* the old way didnt make sure that variable values were used for the name hunt
+ * and didnt ensure that only a full match of the variable name was used
+ */
+
+ do {
+ if(!strncmp(p,name,strlen(name)) && *(p+strlen(name))==':') break;
+ } while((p = (strstr(p,"\n")+1))!=(char *)1);
+
+
+ if (p != (char *)1 && *p!='\0') {
+ char *v, *e;
+
+ if ((v = strchr(p, ':'))) {
+ v++;
+ while(v && *v == ' ') {
+ v++;
+ }
+ if (v) {
+ if (!(e = strchr(v, '\r'))) {
+ e = strchr(v, '\n');
+ }
+ }
+
+ if (v && e) {
+ int cplen;
+ int len = e - v;
+
+ if (len > buflen - 1) {
+ cplen = buflen -1;
+ } else {
+ cplen = len;
+ }
+
+ strncpy(buf, v, cplen);
+ *(buf+cplen) = '\0';
+ return 1;
+ }
+
+ }
+ }
+ return 0;
+}
+
+static int b64encode(unsigned char *in, size_t ilen, unsigned char *out, size_t olen)
+{
+ int y=0,bytes=0;
+ size_t x=0;
+ unsigned int b=0,l=0;
+
+ for(x=0;x<ilen;x++) {
+ b = (b<<8) + in[x];
+ l += 8;
+ while (l >= 6) {
+ out[bytes++] = c64[(b>>(l-=6))%64];
+ if(++y!=72) {
+ continue;
+ }
+ //out[bytes++] = '\n';
+ y=0;
+ }
+ }
+
+ if (l > 0) {
+ out[bytes++] = c64[((b%16)<<(6-l))%64];
+ }
+ if (l != 0) while (l < 6) {
+ out[bytes++] = '=', l += 2;
+ }
+
+ return 0;
+}
+
+#ifdef NO_OPENSSL
+static void sha1_digest(char *digest, unsigned char *in)
+{
+ SHA1Context sha;
+ char *p;
+ int x;
+
+
+ SHA1Init(&sha);
+ SHA1Update(&sha, in, strlen(in));
+ SHA1Final(&sha, digest);
+}
+#else
+
+static void sha1_digest(unsigned char *digest, char *in)
+{
+ SHA_CTX sha;
+
+ SHA1_Init(&sha);
+ SHA1_Update(&sha, in, strlen(in));
+ SHA1_Final(digest, &sha);
+
+}
+
+#endif
+
+int ws_handshake(wsh_t *wsh)
+{
+ char key[256] = "";
+ char version[5] = "";
+ char proto[256] = "";
+ char uri[256] = "";
+ char input[256] = "";
+ unsigned char output[SHA1_HASH_SIZE] = "";
+ char b64[256] = "";
+ char respond[512] = "";
+ ssize_t bytes;
+ char *p, *e;
+
+ if (wsh->sock == ws_sock_invalid) {
+ return -3;
+ }
+
+ if (wsh->secure) {
+ wsh->ssl = SSL_new(globals.ssl_ctx);
+ SSL_set_fd(wsh->ssl, wsh->sock);
+ SSL_accept(wsh->ssl);
+ }
+
+ while((bytes = ws_raw_read(wsh, wsh->buffer + wsh->datalen, wsh->buflen - wsh->datalen)) > 0) {
+ wsh->datalen += bytes;
+ if (strstr(wsh->buffer, "\r\n\r\n") || strstr(wsh->buffer, "\n\n")) {
+ break;
+ }
+ }
+
+ *(wsh->buffer+bytes) = '\0';
+
+ if (strncasecmp(wsh->buffer, "GET ", 4)) {
+ goto err;
+ }
+
+ p = wsh->buffer + 4;
+
+ if (!(e = strchr(p, ' '))) {
+ goto err;
+ }
+
+ strncpy(uri, p, e-p);
+
+ cheezy_get_var(wsh->buffer, "Sec-WebSocket-Key", key, sizeof(key));
+ cheezy_get_var(wsh->buffer, "Sec-WebSocket-Version", version, sizeof(version));
+ cheezy_get_var(wsh->buffer, "Sec-WebSocket-Protocol", proto, sizeof(proto));
+
+ if (!*key) {
+ goto err;
+ }
+
+ snprintf(input, sizeof(input), "%s%s", key, WEBSOCKET_GUID);
+ sha1_digest(output, input);
+ b64encode((unsigned char *)output, SHA1_HASH_SIZE, (unsigned char *)b64, sizeof(b64));
+
+ snprintf(respond, sizeof(respond),
+ "HTTP/1.1 101 Switching Protocols\r\n"
+ "Upgrade: websocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Accept: %s\r\n"
+ "Sec-WebSocket-Protocol: %s\r\n\r\n",
+ b64,
+ proto);
+
+
+ ws_raw_write(wsh, respond, strlen(respond));
+ wsh->handshake = 1;
+
+ return 0;
+
+ err:
+
+ snprintf(respond, sizeof(respond), "HTTP/1.1 400 Bad Request\r\n"
+ "Sec-WebSocket-Version: 13\r\n\r\n");
+
+ //printf("ERR:\n%s\n", respond);
+
+
+ send(wsh->sock, respond, strlen(respond), 0);
+
+ ws_close(wsh, WS_NONE);
+
+ return -1;
+
+}
+
+ssize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes)
+{
+ ssize_t r;
+
+ if (wsh->ssl) {
+ return SSL_read(wsh->ssl, data, bytes);
+ }
+
+ do {
+ r = recv(wsh->sock, data, bytes, 0);
+ } while (r == -1 && (errno == EAGAIN || errno == EINTR));
+
+ if (r<0) {
+ //printf("READ FAIL: %s\n", strerror(errno));
+ }
+
+ return r;
+}
+
+ssize_t ws_raw_write(wsh_t *wsh, void *data, size_t bytes)
+{
+ size_t r;
+
+ if (wsh->ssl) {
+ return SSL_write(wsh->ssl, data, bytes);
+ }
+
+ do {
+ r = send(wsh->sock, data, bytes, 0);
+ } while (r == -1 && (errno == EAGAIN || errno == EINTR));
+
+ if (r<0) {
+ //printf("wRITE FAIL: %s\n", strerror(errno));
+ }
+
+ return r;
+}
+
+int ws_init(wsh_t *wsh, ws_socket_t sock, size_t buflen, int secure)
+{
+ memset(wsh, 0, sizeof(*wsh));
+ wsh->sock = sock;
+
+ if (buflen > MAXLEN) {
+ buflen = MAXLEN;
+ }
+
+ wsh->buflen = buflen;
+ wsh->secure = secure;
+
+ if (!wsh->buffer) {
+ wsh->buffer = malloc(wsh->buflen);
+ assert(wsh->buffer);
+ }
+
+ while (!wsh->down && !wsh->handshake) {
+ ws_handshake(wsh);
+ }
+
+ if (wsh->down) {
+ return -1;
+ }
+
+ return 0;
+}
+
+ssize_t ws_close(wsh_t *wsh, int16_t reason)
+{
+
+ if (wsh->down) {
+ return -1;
+ }
+ wsh->down++;
+
+ if (reason) {
+ uint16_t *u16;
+ uint8_t fr[4] = {WSOC_CLOSE | 0x80, 2, 0};
+
+ u16 = (uint16_t *) &fr[2];
+ *u16 = htons((int16_t)reason);
+ ws_raw_write(wsh, fr, 4);
+ }
+
+
+ if (wsh->ssl) {
+ SSL_free(wsh->ssl);
+ wsh->ssl = NULL;
+ }
+
+ //close(wsh->sock);
+ wsh->sock = ws_sock_invalid;
+
+ if (wsh->buffer) {
+ free(wsh->buffer);
+ wsh->buffer = NULL;
+ }
+
+
+ return reason * -1;
+
+}
+
+ssize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data)
+{
+
+ ssize_t need = 2;
+ char *maskp;
+
+ again:
+ need = 2;
+ maskp = NULL;
+ *data = NULL;
+
+ if (wsh->down) {
+ return -1;
+ }
+
+ if (!wsh->handshake) {
+ return ws_close(wsh, WS_PROTO_ERR);
+ }
+
+ if ((wsh->datalen = ws_raw_read(wsh, wsh->buffer, 14)) < need) {
+ /* too small - protocol err */
+ return ws_close(wsh, WS_PROTO_ERR);
+ }
+
+ *oc = *wsh->buffer & 0xf;
+
+ switch(*oc) {
+ case WSOC_CLOSE:
+ {
+ wsh->plen = wsh->buffer[1] & 0x7f;
+ *data = (uint8_t *) &wsh->buffer[2];
+ return ws_close(wsh, 1000);
+ }
+ break;
+ case WSOC_CONTINUATION:
+ case WSOC_TEXT:
+ case WSOC_BINARY:
+ case WSOC_PING:
+ case WSOC_PONG:
+ {
+ //int fin = (wsh->buffer[0] >> 7) & 1;
+ int mask = (wsh->buffer[1] >> 7) & 1;
+
+ if (mask) {
+ need += 4;
+
+ if (need > wsh->datalen) {
+ /* too small - protocol err */
+ *oc = WSOC_CLOSE;
+ return ws_close(wsh, WS_PROTO_ERR);
+ }
+ }
+
+ wsh->plen = wsh->buffer[1] & 0x7f;
+ wsh->payload = &wsh->buffer[2];
+
+ if (wsh->plen == 127) {
+ uint64_t *u64;
+
+ need += 8;
+
+ if (need > wsh->datalen) {
+ /* too small - protocol err */
+ *oc = WSOC_CLOSE;
+ return ws_close(wsh, WS_PROTO_ERR);
+ }
+
+ u64 = (uint64_t *) wsh->payload;
+ wsh->payload += 8;
+
+ wsh->plen = ntohl(*u64);
+
+ } else if (wsh->plen == 126) {
+ uint16_t *u16;
+
+ need += 2;
+
+ if (need > wsh->datalen) {
+ /* too small - protocol err */
+ *oc = WSOC_CLOSE;
+ return ws_close(wsh, WS_PROTO_ERR);
+ }
+
+ u16 = (uint16_t *) wsh->payload;
+ wsh->payload += 2;
+ wsh->plen = ntohs(*u16);
+ }
+
+ if (mask) {
+ maskp = (char *)wsh->payload;
+ wsh->payload += 4;
+ }
+
+ need = (wsh->plen - (wsh->datalen - need));
+
+ if ((need + wsh->datalen) > wsh->buflen) {
+ /* too big - Ain't nobody got time fo' dat */
+ *oc = WSOC_CLOSE;
+ return ws_close(wsh, WS_DATA_TOO_BIG);
+ }
+
+ wsh->rplen = wsh->plen - need;
+
+ while(need) {
+ ssize_t r = ws_raw_read(wsh, wsh->payload + wsh->rplen, need);
+
+ if (r < 1) {
+ /* invalid read - protocol err .. */
+ *oc = WSOC_CLOSE;
+ return ws_close(wsh, WS_PROTO_ERR);
+ }
+
+ wsh->datalen += r;
+ wsh->rplen += r;
+ need -= r;
+ }
+
+ if (mask && maskp) {
+ uint32_t i;
+
+ for (i = 0; i < wsh->datalen; i++) {
+ wsh->payload[i] ^= maskp[i % 4];
+ }
+ }
+
+
+ if (*oc == WSOC_PING) {
+ ws_write_frame(wsh, WSOC_PONG, wsh->payload, wsh->rplen);
+ goto again;
+ }
+
+
+ *(wsh->payload+wsh->rplen) = '\0';
+ *data = (uint8_t *)wsh->payload;
+
+ //printf("READ[%ld][%d]-----------------------------:\n[%s]\n-------------------------------\n", wsh->rplen, *oc, (char *)*data);
+
+
+ return wsh->rplen;
+ }
+ break;
+ default:
+ {
+ /* invalid op code - protocol err .. */
+ *oc = WSOC_CLOSE;
+ return ws_close(wsh, WS_PROTO_ERR);
+ }
+ break;
+ }
+}
+
+
+ssize_t ws_write_frame(wsh_t *wsh, ws_opcode_t oc, void *data, size_t bytes)
+{
+ uint8_t hdr[14] = { 0 };
+ size_t hlen = 2;
+
+ if (wsh->down) {
+ return -1;
+ }
+
+ //printf("WRITE[%ld]-----------------------------:\n[%s]\n-----------------------------------\n", bytes, (char *) data);
+
+ hdr[0] = oc | 0x80;
+
+ if (bytes < 126) {
+ hdr[1] = bytes;
+ } else if (bytes < 0x10000) {
+ uint16_t *u16;
+
+ hdr[1] = 126;
+ hlen += 2;
+
+ u16 = (uint16_t *) &hdr[2];
+ *u16 = htons((uint16_t) bytes);
+
+ } else {
+ uint64_t *u64;
+
+ hdr[1] = 127;
+ hlen += 8;
+
+ u64 = (uint64_t *) &hdr[2];
+ *u64 = htonl(bytes);
+ }
+
+ if (ws_raw_write(wsh, (void *) &hdr[0], hlen) != hlen) {
+ return -1;
+ }
+
+ if (ws_raw_write(wsh, data, bytes) != bytes) {
+ return -2;
+ }
+
+ return bytes;
+}
+
+
--- /dev/null
+#ifndef _WS_H
+#define _WS_H
+
+#define MAXLEN 0x10000
+#define WEBSOCKET_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+#define B64BUFFLEN 1024
+
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <assert.h>
+#include <errno.h>
+//#include "sha1.h"
+#include <openssl/ssl.h>
+
+
+struct globals_s {
+ const SSL_METHOD *ssl_method;
+ SSL_CTX *ssl_ctx;
+ char cert[512];
+ char key[512];
+};
+
+extern struct globals_s globals;
+
+typedef int ws_socket_t;
+#define ws_sock_invalid -1
+
+
+typedef enum {
+ WS_NONE = 0,
+ WS_NORMAL = 1000,
+ WS_PROTO_ERR = 1002,
+ WS_DATA_TOO_BIG = 1009
+} ws_cause_t;
+
+typedef enum {
+ WSOC_CONTINUATION = 0x0,
+ WSOC_TEXT = 0x1,
+ WSOC_BINARY = 0x2,
+ WSOC_CLOSE = 0x8,
+ WSOC_PING = 0x9,
+ WSOC_PONG = 0xA
+} ws_opcode_t;
+
+typedef struct wsh_s {
+ ws_socket_t sock;
+ char *buffer;
+ size_t buflen;
+ ssize_t datalen;
+ char *payload;
+ ssize_t plen;
+ ssize_t rplen;
+ SSL *ssl;
+ int handshake;
+ uint8_t down;
+ int secure;
+} wsh_t;
+
+ssize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes);
+ssize_t ws_raw_write(wsh_t *wsh, void *data, size_t bytes);
+ssize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data);
+ssize_t ws_write_frame(wsh_t *wsh, ws_opcode_t oc, void *data, size_t bytes);
+int ws_init(wsh_t *wsh, ws_socket_t sock, size_t buflen, int secure);
+ssize_t ws_close(wsh_t *wsh, int16_t reason);
+void init_ssl(void);
+void deinit_ssl(void);
+
+
+static inline uint64_t get_unaligned_uint64(const void *p)
+{
+ const struct { uint64_t d; } __attribute__((packed)) *pp = p;
+ return pp->d;
+}
+
+
+#endif