C websocket library, libwebsockets - c

I am looking through C websocket library libwebsockets client side example.
But i don't understand what the example purpose is.
Here is the example, this example have two connection (in the code wsi_dumb and wsi_mirror)which are same i think, and i don't know what second connection's purpose is.
using first conenction(in the code wsi_dumb), it seems to wait a request from server with libwebsocket_service() and then ...what with second connection(in the code wsi_mirror)?
And below is the part of the code i am saying.
wsi_dumb = libwebsocket_client_connect(context, address, port, use_ssl,
"/", argv[optind], argv[optind],
protocols[PROTOCOL_DUMB_INCREMENT].name, ietf_version);
/*
* sit there servicing the websocket context to handle incoming
* packets, and drawing random circles on the mirror protocol websocket
*/
n = 0;
while (n >= 0 && !was_closed) {
n = libwebsocket_service(context, 1000);
if (wsi_mirror == NULL) {
/* create a client websocket using mirror protocol */
wsi_mirror = libwebsocket_client_connect(context, address, port,
use_ssl, "/", argv[optind], argv[optind],
protocols[PROTOCOL_LWS_MIRROR].name, ietf_version);
mirror_lifetime = 10 + (random() & 1023);
fprintf(stderr, "opened mirror connection with %d lifetime\n", mirror_lifetime);
} else {
mirror_lifetime--;
if (mirror_lifetime == 0) {
fprintf(stderr, "closing mirror session\n");
libwebsocket_close_and_free_session(context,
wsi_mirror, LWS_CLOSE_STATUS_GOINGAWAY);
/*
* wsi_mirror will get set to NULL in
* callback when close completes
*/
}
}
}

I might mix it up but there is an example in libwebsockets where you just open a second browser (window or tab) and then see all the lines and circles you draw in the first browser mirrored and sent to the second browser.

Related

gstreamer: multiple RTSP clients connecting at the same time makes the video stream crash

Quick summary
video stream crashes if multiple clients connect at the same time due to the clients (all but 1) that skip the media-configure callback trying to change the bitrate by accessing a not yet configured pipeline. I'm asking how to wait with calling change_bitrate as long as the configure-media callback hasn't yet finished.
Detailed overview
I'm developing a door phone application that shows video footage of a user (that just rang the door) over the RTSP protocol on one or multiple screens (called clients from now on) in e.g. an appartment building.
When the application is running, it will not create a pipeline before the first client has connected. A new client callback is created in the following way:
/* Configure Callbacks */
/* Create new client handler (Called on new client connect) */
LOG_debug("Creating 'client-connected' signal handler");
g_signal_connect(info.server, "client-connected", G_CALLBACK(new_client_handler), &info);
Which calls this function as soon as a client has connected:
/**
* new_client_handler
* Called by rtsp server on a new client connection
*/
static void new_client_handler(GstRTSPServer *server, GstRTSPClient *client, struct stream_info *si)
{
DEBUG_ENTER;
/* Used to initiate the media-configure callback */
static gboolean first_run = TRUE;
GstRTSPConnection *connection = gst_rtsp_client_get_connection(client);
if (connection == NULL)
{
LOG_err("Could not get RTSP connection");
DEBUG_EXIT;
return;
}
GstRTSPUrl *url = gst_rtsp_connection_get_url(connection);
if (url == NULL)
{
LOG_err("Could not get RTSP connection URL");
DEBUG_EXIT;
return;
}
si->num_cli++;
gchar* uri = gst_rtsp_url_get_request_uri(url);
LOG_info("[%d]A new client %s has connected", si->num_cli, uri);
g_free(uri);
si->connected = TRUE;
/* Create media-configure handler */
/*relevant part for question*/
if (si->num_cli == 1)
{ /* Initial Setup */
/**
* Stream info is required, which is only
* available on the first connection. Stream info is created
* upon the first connection and is never destroyed after that.
*/
if (first_run == TRUE)
{
LOG_debug("Creating 'media-configure' signal handler");
g_signal_connect(si->factory, "media-configure", G_CALLBACK(media_configure_handler),
si);
}
}
else
{
change_bitrate(si); //This makes video stream crash if 'media_configure_handler' isn't yet finished
}
/* Create new client_close_handler */
LOG_debug("Creating 'closed' signal handler");
g_signal_connect(client, "closed", G_CALLBACK(client_close_handler), si);
first_run = FALSE;
DEBUG_EXIT;
}
When a client is the first one to connect, it sets up the media-configure callback to initialize the pipeline. The configuration code looks like this:
**
* media_configure_handler
* Setup pipeline when the stream is first configured
*/
static void media_configure_handler(GstRTSPMediaFactory *factory, GstRTSPMedia *media,
struct stream_info *si)
{
DEBUG_ENTER;
si->media = media;
LOG_info("[%d]Configuring pipeline...", si->num_cli);
si->pipeline = GST_BIN(gst_rtsp_media_get_element(media)); //Pipeline gets configured here
setup_elements(si);
if (si->num_cli == 1)
{
/* Create Msg Event Handler */
LOG_debug("Creating 'periodic message' handler");
g_timeout_add(si->msg_rate * 1000, (GSourceFunc) periodic_msg_handler, si);
}
DEBUG_EXIT;
}
A second (or nth) client that connects skips the media configuration step and instead goes to change_bitrate. Here the bitrate is adjusted based on the amount of connected clients.
/**
* change_bitrate
* handle changing of bitrates
*/
static void change_bitrate(struct stream_info *si)
{
DEBUG_ENTER;
int c = si->curr_bitrate;
int step = (si->max_bitrate - si->min_bitrate) / si->steps;
GstElement *elem = search_pipeline(si->pipeline, "enc"); //crashes due to an unitialized pipeline
const gchar *name = g_ascii_strdown(G_OBJECT_TYPE_NAME(elem), -1);
GstStructure *extra_controls;
...
}
This all works fine if a single client connects first. Later, the connection can handle multiple clients and adjusts the bitrate accordingly.
The problem arises if the first connection is by multiple clients:
In this case, both clients enter an instance of new_client_handler, in which the first one will set up the media_configure_handler. The second connection tries to change the bitrate, but fails because the pipeline is not yet configured by the callback.
How can i make the second (and nth) connection wait until the media configure callback has finished and thus a pipeline is available?
Solved this in the end with the following code (in function new_client_handler)
/* Create media-configure handler */
if (si->num_cli == 1)
{ /* Initial Setup */
/**
* Stream info is required, which is only
* available on the first connection. Stream info is created
* upon the first connection and is never destroyed after that.
*/
if (first_run == TRUE)
{
LOG_debug("Creating 'media-constructor' signal handler");
g_signal_connect(si->factory, "media-constructed", G_CALLBACK(media_configure_handler),
si);
}
}
else if(si->pipeline != 0)
{
change_bitrate(si);
}
else
{
g_signal_connect(si->factory, "media-configure", G_CALLBACK(media_constructed_handler),
si);
}
Pipeline object's construction is now hooked to the media-constructed event, which runs before the media-configure event.
A second client will only change bitrate if pipeline is initialized. If not, the client hooks in the media-configure callback and changes bitrate there. This callback is guaranteed to run after the media-constructed callback.

gstreamer 1.14.5 multiple rtspsrc element pipeline, reconnect individual streams when disconnected via 'C' code

Hello GStreamer community & fans,
I have a working pipeline that connects to multiple H.264 IP camera streams using multiple rtspsrc elements aggregated into a single pipeline for downstream video processing.
Intermittently & randomly, streams coming in from remote & slower connections will have problems, timeout, retry and go dead, leaving that stream with a black image when viewing the streams post processing. The other working streams continue to process normally. The rtspsrc elements are setup to retry the rtsp connection, and that seems to somewhat work, but for when it doesn't, I'm looking for a way to disconnect the stream entirely from the rtspsrc element and restart that particular stream without disrupting the other streams.
I haven't found any obvious examples or ways to accomplish this, so I've been tinkering with the rtspsrc element code itself using this public function to access the rtspsrc internals that handle connecting.
__attribute__ ((visibility ("default"))) GstRTSPResult my_gst_rtspsrc_conninfo_reconnect(GstRTSPSrc *, gboolean);
GstRTSPResult
my_gst_rtspsrc_conninfo_reconnect(GstRTSPSrc *src, gboolean async)
{
int retries = 0, port = 0;
char portrange_buff[32];
// gboolean manual_http;
GST_ELEMENT_WARNING(src, RESOURCE, READ, (NULL),
(">>>>>>>>>> Streamer: A camera closed the streaming connection. Trying to reconnect"));
gst_rtspsrc_set_state (src, GST_STATE_PAUSED);
gst_rtspsrc_set_state (src, GST_STATE_READY);
gst_rtspsrc_flush(src, TRUE, FALSE);
// manual_http = src->conninfo.connection->manual_http;
// src->conninfo.connection->manual_http = TRUE;
gst_rtsp_connection_set_http_mode(src->conninfo.connection, TRUE);
if (gst_rtsp_conninfo_close(src, &src->conninfo, TRUE) == GST_RTSP_OK)
{
memset(portrange_buff, 0, sizeof(portrange_buff));
g_object_get(G_OBJECT(src), "port-range", portrange_buff, NULL);
for (retries = 0; portrange_buff[retries] && isdigit(portrange_buff[retries]); retries++)
port = (port * 10) + ((int)(portrange_buff[retries]) + 48);
if (port != src->client_port_range.min)
GST_ELEMENT_WARNING(src, RESOURCE, READ, (NULL), (">>>>>>>>>> Streamer: port range start mismatch"));
GST_WARNING_OBJECT(src, ">>>>>>>>>> Streamer: old port.min: %d, old port.max: %d, old port-range: %s\n", (src->client_port_range.min), (src->client_port_range.max), (portrange_buff));
src->client_port_range.min += 6;
src->client_port_range.max += 6;
src->next_port_num = src->client_port_range.min;
memset(portrange_buff, 0, sizeof(portrange_buff));
sprintf(portrange_buff, "%d-%d", src->client_port_range.min, src->client_port_range.max);
g_object_set(G_OBJECT(src), "port-range", portrange_buff, NULL);
for (retries = 0; retries < 5 && gst_rtsp_conninfo_connect(src, &src->conninfo, async) != GST_RTSP_OK; retries++)
sleep(10);
}
if (retries < 5)
{
gst_rtspsrc_set_state(src, GST_STATE_PAUSED);
gst_rtspsrc_set_state(src, GST_STATE_PLAYING);
return GST_RTSP_OK;
}
else return GST_RTSP_ERROR;
}
I realize this is probably not best practice and I'm doing this to find a better way once I understand the internals better through this learning experience.
I appreciate any feedback anyone has to this problem.
-Doug

porting synproxy to user space

I have ported the kernel's synproxy code to user space. I use it as a transparent proxy between the client and the web server.
When I request a web page, it works fine (the web page is below 512kbit). However, if I access a huge file (4Gbit or more), it will not continue to transfer after 3Gbits is transmitted.
I have adjusted the "confirmation" sent by the server to the client and the "seq" sent from the client to the server. At the same time, the tcp option "sack" sent from the client to the server is also adjusted.
if(tcpinp->state == CONNTRACK_SYN_SENT){
if(tcphdr->tcp_flags == (TCP_SYN_FLAG|TCP_ACK_FLAG)){
synproxy_parse_options(tcphdr, &opts);
tcpinp->tsoff = opts.tsval - tcpinp->its;
}
swap(opts.tsval, opts.tsecr);
synproxy_send_server_ack(iphdr,tcphdr, &opts);
/* send client ack,update tcp window */
swap(opts.tsval, opts.tsecr);
/*
* update window to client
*
* dir:server -> client
*
* save isn_off = client.ISN1 - server.ISN2
*/
tcpinp->isn_off = tcpinp->isn - tcphdr->seq;
synproxy_send_client_ack(tcpinp, &opts);
tcpinp->state = CONNTRACK_ESTABLISHED;
return 0;
}
if(tcpinp->dir == IP_CT_DIR_REPLY){
tcphdr->sent_seq = htonl(ntohl(tcphdr->sent_seq) + tcpinp->isn_off);
}else if(tcpinp->dir == IP_CT_DIR_ORIGINAL){
tcphdr->recv_ack = htonl(ntohl(tcphdr->recv_ack) - tcpinp->isn_off);
nf_ct_sack_adjust(tcph, other_way);
}
What should I adjust besides seq / ack and sack?
---I found that the client tcp'win' option has been increased to '828800' and then it does not change.
I have solved ,that it need 'mss' is always same.

SDLNet Networking Not Working

I am working on a game written in C using SDL. Given that it already uses SDL, SDL_image, and SDL_ttf, I decided to add SDL_mixer and SDL_net to my engine. Getting SDL_mixer set up and working was very easy, but I am having a lot of trouble with SDL_net.
To test I created a very simple application with the following rules:
Run without arguments act as a TCP server on port 9999
Run with an argument try to connect to the server at the given IP address on port 9999
Here are some of the key lines of the program (I'm not going to post my whole event-driven SDL engine because its too long):
char *host = NULL;
if (argc > 1) host = argv[1];
and...
IPaddress ip;
TCPsocket server = NULL;
TCPsocket conn = NULL;
if (host) { /* client mode */
if (SDLNet_ResolveHost(&ip,host,port) < 0)
return NULL; //this is actually inside an engine method
if (!(conn = SDLNet_TCP_Open(&ip)))
return NULL;
} else { /* server mode */
if (SDLNet_ResolveHost(&ip,NULL,port) < 0)
return NULL;
if (!(server = SDLNet_TCP_Open(&ip)))
return NULL;
}
and... inside the event loop
if (server) {
if (!conn)
conn = SDLNet_TCP_Accept(server);
}
if (conn) {
void *buf = malloc(size); //server, conn, size are actually members of a weird struct
while (SDLNet_TCP_Recv(conn,buf,size))
onReceive(buf); //my engine uses a callback system to handle things
free(buf);
}
The program seems to start up just fine. However for some reason when I run it in client mode trying to connect to my home computer (which I have on a different IP) from my laptop I find that the call to SDLNet_TCP_Open blocks the program for awhile (5-10 seconds) then returns NULL. Can anybody see what I did wrong? Should I post more of the code? Let me know.

SSL_CONNECT fails with SSL_ERROR_SYSCALL error

Having strange connection failure with openssl SSLConnect with my SSLCLIENT.
We are trying to establish ssl connection with a server. We notice that SSL_CONNECT is failing with error code "SSL_ERROR_SYSCALL".
For further depth we tried printing strerror(errno) which return "scuccess" "0".
However i am just trying to understand what might be the exact cause for this issue
Added code snippet for SSL init and connect::
request some guidance:
int setupSSL(int server){
int retVal = 0;
int errorStatus = 0;
int retryMaxCount = 6;
static int sslInitContext=0;
if(sslInitContext == 0)
{
if(InitCTX() != 0)
{
return -1;
}
else
{
sslInitContext=1;
}
}
retVal = SSL_set_fd(ssl, server); /* attach the socket descriptor */
if ( retVal != 1 )
{
/* perform the connection */
sprintf(debugBuf,"SYSTEM:SOCKET:Could not set ssl FD: %d %s\n",retVal,strerror(retVal));
debug_log(debugBuf,TRACE_LOG);
CloseSocket(server);
return -1;
}
do
{
retVal = SSL_connect(ssl);
errorStatus = SSL_get_error (ssl, retVal);
switch (errorStatus)
{
case SSL_ERROR_NONE:
retVal = 0;
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
retVal = 1;
break;
default:
sprintf(debugBuf,"SYSTEM:SSL_SOCKET:Could not build SSL session(Other error): %d %s\n",errorStatus,strerror(errno));
debug_log(debugBuf,TRACE_LOG);
CloseSocket(server);
return -1;
}
sprintf(debugBuf,"SYSTEM:SSL_SOCKET:SSL CONNECTION Under PROGRESS: %d with remaining retries %d\n",errorStatus,retryMaxCount);
debug_log(debugBuf,TRACE_LOG);
if (retVal)
{
struct timeval tv;
fd_set sockReadSet;
tv.tv_sec = 2;
tv.tv_usec = 0;
FD_ZERO(&sockReadSet);
FD_CLR(server, &sockReadSet);
FD_SET(server,&sockReadSet);
retVal = select(server+1, &sockReadSet, NULL, NULL, &tv);
if (retVal >= 1)
{
retVal = 1;
}
else
{
retVal = -1;
}
retryMaxCount--;
if (retryMaxCount <= 0 )
break;
}
}while(!SSL_is_init_finished (ssl) && retVal == 1);
cert = SSL_get_peer_certificate(ssl);
if(cert == NULL)
{
debug_log("SYSTEM:SSL_SOCKET:Unable to retrive server certificate\n",TRACE_LOG);
CloseSocket(server);
return -1;
}
if(SSL_get_verify_result(ssl)!=X509_V_OK)
{
debug_log("SYSTEM:SSL_SOCKET:Certificate doesn't verify\n",TRACE_LOG);
CloseSocket(server);
return -1;
}
/*X509_NAME_get_text_by_NID (X509_get_subject_name (cert), NID_commonName, peer_CN, 256);
if(strcasecmp(peer_CN, cnName)){
debug_log("SYSTEM:SSL_SOCKET:Common name doesn't match host name\n",TRACE_LOG);
return -1;
}*/
return 0;
// LoadCertificates(ctx, CertFile, KeyFile);
}
int InitCTX(void)
{
int errorStatus = 0;
static int isSslInit = 1;
if(isSslInit)
{
OpenSSL_add_all_algorithms();/* Load cryptos, et.al. */
SSL_load_error_strings();/* Bring in and register error messages */
if(SSL_library_init() < 0)
{
debug_log("SYSTEM:SSL_SOCKET:Could not initialize the OpenSSL library\n",TRACE_LOG);
return -1;
}
method = TLSv1_client_method();
isSslInit=0;
}
ctx = SSL_CTX_new(method);/* Create new context */
if ( ctx == NULL)
{
debug_log("SYSTEM:SSL_SOCKET:Unable to create a new SSL context structure\n",TRACE_LOG);
//sprintf(debugBuf,"SYSTEM:SSL_SOCKET:Unable to create a new SSL context structure: %d %s\n",errorStatus,strerror(retVal));
//debug_log(debugBuf,TRACE_LOG);
return -1;
}
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
if (SSL_CTX_use_certificate_file(ctx,CertFile, SSL_FILETYPE_PEM) <= 0)
{
SSL_CTX_free(ctx);
ctx = NULL;
debug_log("SYSTEM:SSL_SOCKET:Error setting the certificate file.\n",TRACE_LOG);
return -1;
}
/* Set the list of trusted CAs based on the file and/or directory provided*/
if(SSL_CTX_load_verify_locations(ctx,CertFile,NULL)<1)
{
SSL_CTX_free(ctx);
ctx = NULL;
debug_log("SYSTEM:SSL_SOCKET:Error setting verify location.\n",TRACE_LOG);
return -1;
}
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);
SSL_CTX_set_timeout (ctx, 300);
ssl = SSL_new(ctx); /* create new SSL connection state */
if(ssl == NULL)
{
sprintf(debugBuf,"SYSTEM:SOCKET:SSL:Unable to create SSL_new context\n");
debug_log(debugBuf,DEBUG_LOG);
if(ctx != NULL)
SSL_CTX_free(ctx);
return -1;
}
return 0;
}
Also is it advised to maintain SSL context for new connections or should we destroy and re init the ssl context??
Added PCAP info:
https://drive.google.com/file/d/0B60pejPe6yiSUk1MMmI1cERMaFU/view?usp=sharing
client: 198.168.51.10 (198.168.51.10), Server: 192.168.96.7 (192.168.96.7)
We are trying to establish ssl connection with a server. We notice that SSL_CONNECT is failing with error code "SSL_ERROR_SYSCALL".
This is usually the case if the other side is simply closing the connection. Microsoft SChannel does this on many kind of handshake problems instead of sending a TLS alert back. This can happen for problems like invalid protocol or no common ciphers etc. It also can happen if you try to do a TLS handshake with a server which does not speak TLS at all on this port. Look at logs at the server side for problems.
Of course it can also be something different so you might check the errno to get more details about the problem. It might also help if you do a packet capture to check what's going on on the wire. Best would be to do this capture on the client and server side to make sure that no middlebox like a firewall is tampering with the connection.
Also is it advised to maintain SSL context for new connections or should we destroy and re init the ssl context??
The context is just a collection of settings, certificates etc and is not affected by the SSL connection itself. You can reuse it for other connection later or at the same time.
EDIT, after the packet capture was attached:
There are multiple TCP connection in the file between client and server and only inside a single one the client tries to initiate a handshake, i.e. the ClientHello can be seen. The server closes the connection. A few things a interesting:
TCP handshake takes very long. The server only replies after 1.6 seconds after receiving the SYN with the SYN+ACK. Also the other replies take 800ms which is very long given that both addresses are in a private network (192.168.0.0). This might indicate a slow connection or VPN (this is about the latency of a satellite link), some middlebox (firewall) slowing everything down or a really slow server.
Client sends TLS 1.0 request. It might be that the server will do only TLS 1.1+. Some TLS stacks (see above) simply close the connection on such errors instead of sending an unsupported protocol alert. But given that the server is slow it might also be old and only support SSL 3.0 or lower.
Client does not use SNI extension. More and more servers need this and might simply close if they don't get the extension.
It is hard to know what really is going on without having access to the server. I recommend to look for error messages on the server side and use tools like SSLyze to check the requirements of the server, i.e. supported TLS versions, ciphers etc.
Apart from that client offers dangerously weak ciphers like various EXPORT ciphers. This looks for me like the defaults of a considerably old OpenSSL version.

Resources