1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00
This commit is contained in:
Andy Green 2018-03-24 08:07:00 +08:00
parent 1820212724
commit 7cef6fcc7b
8 changed files with 206 additions and 21 deletions

View file

@ -743,7 +743,9 @@ callbacks on the named protocol
starting with LWS_CALLBACK_RAW_ADOPT_FILE.
`protocol-lws-raw-test` plugin provides a method for testing this with
The minimal example `raw/minimal-raw-file` demonstrates how to use it.
`protocol-lws-raw-test` plugin also provides a method for testing this with
`libwebsockets-test-server-v2.0`:
The plugin creates a FIFO on your system called "/tmp/lws-test-raw"
@ -827,6 +829,46 @@ and in another window, connect to it using the test client
The connection should succeed, and text typed in the netcat window (including a CRLF)
will be received in the client.
@section rawudp RAW UDP socket integration
Lws provides an api to create, optionally bind, and adopt a RAW UDP
socket (RAW here means an uninterpreted normal UDP socket, not a
"raw socket").
```
LWS_VISIBLE LWS_EXTERN struct lws *
lws_create_adopt_udp(struct lws_vhost *vhost, int port, int flags,
const char *protocol_name, struct lws *parent_wsi);
```
`flags` should be `LWS_CAUDP_BIND` if the socket will receive packets.
The callbacks `LWS_CALLBACK_RAW_ADOPT`, `LWS_CALLBACK_RAW_CLOSE`,
`LWS_CALLBACK_RAW_RX` and `LWS_CALLBACK_RAW_WRITEABLE` apply to the
wsi. But UDP is different than TCP in some fundamental ways.
For receiving on a UDP connection, data becomes available at
`LWS_CALLBACK_RAW_RX` as usual, but because there is no specific
connection with UDP, it is necessary to also get the source address of
the data separately, using `struct lws_udp * lws_get_udp(wsi)`.
You should take a copy of the `struct lws_udp` itself (not the
pointer) and save it for when you want to write back to that peer.
Writing is also a bit different for UDP. By default, the system has no
idea about the receiver state and so asking for a `callback_on_writable()`
always believes that the socket is writeable... the callback will
happen next time around the event loop.
With UDP, there is no single "connection". You need to write with sendto() and
direct the packets to a specific destination. To return packets to a
peer who sent something earlier and you copied his `struct lws_udp`, you
use the .sa and .salen members as the last two parameters of the sendto().
The kernel may not accept to buffer / write everything you wanted to send.
So you are responsible to watch the result of sendto() and resend the
unsent part next time (which may involve adding new protocol headers to
the remainder depending on what you are doing).
@section ecdh ECDH Support
ECDH Certs are now supported. Enable the CMake option

View file

@ -565,6 +565,7 @@ lws_create_vhost(struct lws_context *context,
#endif
struct lws_protocols *lwsp;
int m, f = !info->pvo;
char buf[20];
#ifdef LWS_HAVE_GETENV
char *p;
#endif
@ -721,10 +722,22 @@ lws_create_vhost(struct lws_context *context,
vh->name, vh->iface, vh->count_protocols);
} else
#endif
lwsl_notice("Creating Vhost '%s' port %d, %d protocols, IPv6 %s\n",
vh->name, info->port, vh->count_protocols,
LWS_IPV6_ENABLED(vh) ? "on" : "off");
{
switch(info->port) {
case CONTEXT_PORT_NO_LISTEN:
strcpy(buf, "(serving disabled)");
break;
case CONTEXT_PORT_NO_LISTEN_SERVER:
strcpy(buf, "(no listener)");
break;
default:
lws_snprintf(buf, sizeof(buf), "port %u", info->port);
break;
}
lwsl_notice("Creating Vhost '%s' %s, %d protocols, IPv6 %s\n",
vh->name, buf, vh->count_protocols,
LWS_IPV6_ENABLED(vh) ? "on" : "off");
}
mounts = info->mounts;
while (mounts) {
(void)mount_protocols[0];

View file

@ -80,6 +80,7 @@ __lws_free_wsi(struct lws *wsi)
lws_free_set_NULL(wsi->rxflow_buffer);
lws_free_set_NULL(wsi->trunc_alloc);
lws_free_set_NULL(wsi->ws);
lws_free_set_NULL(wsi->udp);
/* we may not have an ah, but may be on the waiting list... */
lwsl_info("ah det due to close\n");
@ -1330,6 +1331,12 @@ lws_protocol_get(struct lws *wsi)
return wsi->protocol;
}
LWS_VISIBLE const struct lws_udp *
lws_get_udp(const struct lws *wsi)
{
return wsi->udp;
}
LWS_VISIBLE struct lws *
lws_get_network_wsi(struct lws *wsi)
{
@ -2694,12 +2701,71 @@ lws_get_addr_scope(const char *ipaddr)
}
#endif
#if !defined(LWS_NO_SERVER)
LWS_EXTERN struct lws *
lws_create_adopt_udp(struct lws_vhost *vhost, int port, int flags,
const char *protocol_name, struct lws *parent_wsi)
{
lws_sock_file_fd_type sock;
struct addrinfo h, *r, *rp;
struct lws *wsi = NULL;
char buf[16];
int n;
memset(&h, 0, sizeof(h));
h.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
h.ai_socktype = SOCK_DGRAM;
h.ai_protocol = IPPROTO_UDP;
h.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
lws_snprintf(buf, sizeof(buf), "%u", port);
n = getaddrinfo(NULL, buf, &h, &r);
if (n) {
lwsl_info("%s: getaddrinfo error: %s\n", __func__,
gai_strerror(n));
goto bail;
}
for (rp = r; rp; rp = rp->ai_next) {
sock.sockfd = socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (sock.sockfd >= 0)
break;
}
if (!rp) {
lwsl_err("%s: unable to create INET socket\n", __func__);
goto bail1;
}
if ((flags & LWS_CAUDP_BIND) &&
bind(sock.sockfd, rp->ai_addr, rp->ai_addrlen) ==-1) {
lwsl_err("%s: bind failed\n", __func__);
goto bail2;
}
wsi = lws_adopt_descriptor_vhost(vhost, LWS_ADOPT_RAW_SOCKET_UDP, sock,
protocol_name, parent_wsi);
if (!wsi)
lwsl_err("%s: udp adoption failed\n", __func__);
bail2:
if (!wsi)
close(sock.sockfd);
bail1:
freeaddrinfo(r);
bail:
return wsi;
}
#endif
LWS_EXTERN void
lws_restart_ws_ping_pong_timer(struct lws *wsi)
{
if (!wsi->context->ws_ping_pong_interval)
return;
if (!lws_state_is_ws(wsi->state))
if (!wsi->context->ws_ping_pong_interval ||
!lws_state_is_ws(wsi->state))
return;
wsi->ws->time_next_ping_check = (time_t)lws_now_secs();

View file

@ -4555,6 +4555,7 @@ enum pending_timeout {
PENDING_TIMEOUT_KILLED_BY_PARENT = 23,
PENDING_TIMEOUT_CLOSE_SEND = 24,
PENDING_TIMEOUT_HOLDING_AH = 25,
PENDING_TIMEOUT_UDP_IDLE = 26,
/****** add new things just above ---^ ******/
@ -4998,7 +4999,7 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
/**
* lws_get_socket_fd() - returns the socket file descriptor
*
* You will not need this unless you are doing something special
* This is needed to use sendto() on UDP raw sockets
*
* \param wsi: Websocket connection instance
*/
@ -5151,8 +5152,10 @@ typedef enum {
LWS_ADOPT_ALLOW_SSL = 4, /* flag: if set requires LWS_ADOPT_SOCKET */
LWS_ADOPT_WS_PARENTIO = 8, /* flag: ws mode parent handles IO
* if given must be only flag
* wsi put directly into ws mode
*/
* wsi put directly into ws mode */
LWS_ADOPT_FLAG_UDP = 16, /* flag: socket is UDP */
LWS_ADOPT_RAW_SOCKET_UDP = LWS_ADOPT_SOCKET | LWS_ADOPT_FLAG_UDP,
} lws_adoption_type;
typedef union {
@ -5160,6 +5163,14 @@ typedef union {
lws_filefd_type filefd;
} lws_sock_file_fd_type;
struct lws_udp {
struct sockaddr sa;
socklen_t salen;
struct sockaddr sa_pending;
socklen_t salen_pending;
};
/*
* lws_adopt_descriptor_vhost() - adopt foreign socket or file descriptor
* if socket descriptor, should already have been accepted from listen socket
@ -5236,6 +5247,24 @@ lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd,
LWS_VISIBLE LWS_EXTERN struct lws *
lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost, lws_sockfd_type accept_fd,
const char *readbuf, size_t len);
#define LWS_CAUDP_BIND 1
/**
* lws_create_adopt_udp() - create, bind and adopt a UDP socket
*
* \param vhost: lws vhost
* \param port: UDP port to bind to, -1 means unbound
* \param flags: 0 or LWS_CAUDP_NO_BIND
* \param protocol_name: Name of protocol on vhost to bind wsi to
* \param parent_wsi: NULL or parent wsi new wsi will be a child of
*
* Either returns new wsi bound to accept_fd, or closes accept_fd and
* returns NULL, having cleaned up any new wsi pieces.
* */
LWS_VISIBLE LWS_EXTERN struct lws *
lws_create_adopt_udp(struct lws_vhost *vhost, int port, int flags,
const char *protocol_name, struct lws *parent_wsi);
///@}
/** \defgroup net Network related helper APIs
@ -5632,6 +5661,16 @@ lws_get_parent(const struct lws *wsi);
LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
lws_get_child(const struct lws *wsi);
/**
* lws_get_udp() - get wsi's udp struct
*
* \param wsi: lws connection
*
* Returns NULL or pointer to the wsi's UDP-specific information
*/
LWS_VISIBLE LWS_EXTERN const struct lws_udp * LWS_WARN_UNUSED_RESULT
lws_get_udp(const struct lws *wsi);
/**
* lws_parent_carries_io() - mark wsi as needing to send messages via parent
*

View file

@ -198,6 +198,12 @@ handle_truncated_send:
wsi->trunc_len = (unsigned int)(real_len - n);
memcpy(wsi->trunc_alloc, buf + n, real_len - n);
if (lws_wsi_is_udp(wsi)) {
/* stash original destination for fulfilling UDP partials */
wsi->udp->sa_pending = wsi->udp->sa;
wsi->udp->salen_pending = wsi->udp->salen;
}
/* since something buffered, force it to get another chance to send */
lws_callback_on_writable(wsi);
@ -858,12 +864,19 @@ lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
n = recv(wsi->desc.sockfd, (char *)buf, len, 0);
if (lws_wsi_is_udp(wsi)) {
wsi->udp->salen = sizeof(wsi->udp->sa);
n = recvfrom(wsi->desc.sockfd, (char *)buf, len, 0,
&wsi->udp->sa, &wsi->udp->salen);
} else
n = recv(wsi->desc.sockfd, (char *)buf, len, 0);
if (n >= 0) {
if (wsi->vhost)
wsi->vhost->conn_stats.rx += n;
lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
lws_restart_ws_ping_pong_timer(wsi);
return n;
}
#if LWS_POSIX
@ -882,7 +895,13 @@ lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
int n = 0;
#if LWS_POSIX
n = send(wsi->desc.sockfd, (char *)buf, len, MSG_NOSIGNAL);
if (lws_wsi_is_udp(wsi)) {
if (wsi->trunc_len)
n = sendto(wsi->desc.sockfd, buf, len, 0, &wsi->udp->sa_pending, wsi->udp->salen_pending);
else
n = sendto(wsi->desc.sockfd, buf, len, 0, &wsi->udp->sa, wsi->udp->salen);
} else
n = send(wsi->desc.sockfd, (char *)buf, len, MSG_NOSIGNAL);
// lwsl_info("%s: sent len %d result %d", __func__, len, n);
if (n >= 0)
return n;

View file

@ -1840,6 +1840,8 @@ struct lws_access_log {
};
#endif
#define lws_wsi_is_udp(___wsi) (!!___wsi->udp)
struct lws {
/* structs */
@ -1881,6 +1883,7 @@ struct lws {
#endif
struct allocated_headers *ah;
struct lws *ah_wait_list;
struct lws_udp *udp;
unsigned char *preamble_rx;
#ifndef LWS_NO_CLIENT
struct client_info_stash *stash;

View file

@ -70,10 +70,9 @@ lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd)
}
#endif
rlen = sizeof(addr);
if (getpeername(sockfd, (struct sockaddr*)&addr, &rlen)) {
lwsl_notice("%s: getpeername failed\n", __func__);
if (getpeername(sockfd, (struct sockaddr*)&addr, &rlen))
/* eg, udp doesn't have to have a peer */
return NULL;
}
if (af == AF_INET) {
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
@ -111,6 +110,7 @@ lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd)
peer = lws_zalloc(sizeof(*peer), "peer");
if (!peer) {
lws_context_unlock(context); /* === */
lwsl_err("%s: OOM for new peer\n", __func__);
return NULL;
}

View file

@ -2012,11 +2012,7 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
if (type & LWS_ADOPT_SOCKET && !(type & LWS_ADOPT_WS_PARENTIO)) {
peer = lws_get_or_create_peer(vh, fd.sockfd);
if (!peer) {
lwsl_err("OOM creating peer\n");
return NULL;
}
if (context->ip_limit_wsi &&
if (peer && context->ip_limit_wsi &&
peer->count_wsi >= context->ip_limit_wsi) {
lwsl_notice("Peer reached wsi limit %d\n",
context->ip_limit_wsi);
@ -2092,6 +2088,13 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
lwsl_debug("%s: new wsi %p, sockfd %d\n", __func__, new_wsi,
(int)(lws_intptr_t)fd.sockfd);
if (type & LWS_ADOPT_FLAG_UDP)
/*
* these can be >128 bytes, so just alloc for UDP
*/
new_wsi->udp = lws_malloc(sizeof(*new_wsi->udp),
"udp struct");
if (type & LWS_ADOPT_HTTP)
/* the transport is accepted...
* give him time to negotiate */