add websocket transport to sofia
This commit is contained in:
parent
a70aa8f9be
commit
62e62e1e34
|
@ -234,6 +234,8 @@ struct nta_agent_s
|
|||
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 */
|
||||
|
@ -2045,22 +2047,24 @@ struct sipdns_tport {
|
|||
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] =
|
||||
|
@ -2188,7 +2192,7 @@ int nta_agent_add_tport(nta_agent_t *self,
|
|||
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++) {
|
||||
|
@ -2311,6 +2315,8 @@ int agent_init_via(nta_agent_t *self, tport_t *primaries, int use_maddr)
|
|||
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)) {
|
||||
|
@ -2343,6 +2349,10 @@ int agent_init_via(nta_agent_t *self, tport_t *primaries, int use_maddr)
|
|||
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;
|
||||
|
||||
|
@ -2684,8 +2694,12 @@ nta_tpn_by_url(su_home_t *home,
|
|||
|
||||
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";
|
||||
|
|
|
@ -44,7 +44,7 @@ if HAVE_STUN
|
|||
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
|
||||
|
|
|
@ -202,7 +202,7 @@ int tport_is_registered(tport_t const *self)
|
|||
/** 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. */
|
||||
|
@ -1345,10 +1345,12 @@ int tport_set_params(tport_t *self,
|
|||
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;
|
||||
|
@ -1359,6 +1361,8 @@ tport_vtable_t const *tport_vtables[TPORT_NUMBER_OF_TYPES + 1] =
|
|||
{
|
||||
#if HAVE_SOFIA_NTH
|
||||
&tport_http_connect_vtable,
|
||||
&tport_ws_client_vtable,
|
||||
&tport_ws_vtable,
|
||||
#endif
|
||||
#if HAVE_TLS
|
||||
&tport_tls_client_vtable,
|
||||
|
@ -2426,6 +2430,13 @@ int getprotohints(su_addrinfo_t *hints,
|
|||
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;
|
||||
|
@ -2869,7 +2880,7 @@ void tport_recv_event(tport_t *self)
|
|||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -161,6 +161,7 @@ struct tport_s {
|
|||
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 */
|
||||
|
@ -527,6 +528,10 @@ void tport_recv_timeout_timer(tport_t *self, su_time_t now);
|
|||
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;
|
||||
|
|
|
@ -382,6 +382,8 @@ void tport_capt_msg(tport_t const *self, msg_t *msg, size_t n,
|
|||
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 */
|
||||
|
|
|
@ -0,0 +1,413 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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
|
|
@ -0,0 +1,585 @@
|
|||
#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;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
#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
|
Loading…
Reference in New Issue