cnping

Minimal Graphical Ping Tool
git clone https://git.sinitax.com/cnlohr/cnping
Log | Files | Refs | Submodules | README | LICENSE | sfeed.txt

httping.c (6022B)


      1#include "httping.h"
      2#include <errno.h>
      3#include <stdint.h>
      4#include <stdio.h>
      5#include <stdlib.h>
      6#include <string.h>
      7#include "resolve.h"
      8
      9#ifndef TCC
     10#include <unistd.h>
     11#include <sys/types.h>
     12#else
     13#include "tccheader.h"
     14#endif
     15
     16#if defined( WIN32 ) || defined( WINDOWS )
     17	#ifdef TCC
     18	#else
     19	#include <ws2tcpip.h>
     20	#endif
     21	#define SOL_TCP IPPROTO_TCP
     22#else
     23	#include <sys/socket.h>
     24	#include <netinet/in.h>
     25	#include <netinet/tcp.h>
     26	#include <netdb.h>
     27#endif
     28
     29#include "rawdraw/os_generic.h"
     30#include "error_handling.h"
     31
     32#ifndef MSG_NOSIGNAL
     33	#define MSG_NOSIGNAL 0
     34#endif
     35
     36#define HTTPTIMEOUT 3.0
     37
     38//Callbacks (when started/received)
     39void HTTPingCallbackStart( int seqno );
     40void HTTPingCallbackGot( int seqno );
     41
     42//Don't dynamically allocate resources here, since execution may be stopped arbitrarily.
     43void DoHTTPing( const char * addy, double minperiod, int * seqnoptr, volatile double * timeouttime, int * socketptr, volatile int * getting_host_by_name, const char * device)
     44{
     45#if defined(WIN32) || defined(WINDOWS)
     46	(void) device; // option is not available for windows. Suppress unused warning.
     47
     48	WSADATA wsaData;
     49	int r =	WSAStartup(MAKEWORD(2,2), &wsaData);
     50	if( r )
     51	{
     52		ERRM( "Fault in WSAStartup\n" );
     53		exit( -2 );
     54	}
     55#endif
     56	struct sockaddr_in6 serveraddr;
     57	socklen_t serveraddr_len;
     58	int serverresolve;
     59	int httpsock;
     60	int addylen = strlen(addy);
     61	char hostname[addylen+1];
     62	memcpy( hostname, addy, addylen + 1 );
     63	char * eportmarker = strchr( hostname, ':' );
     64	char * eurl = strchr( hostname, '/' );
     65
     66	int portno = 80;
     67
     68	if( eportmarker )
     69	{
     70		portno = atoi( eportmarker+1 );
     71		*eportmarker = 0;
     72	}
     73	else
     74	{
     75		if( eurl )
     76			*eurl = 0;
     77	}
     78
     79	/* gethostbyname: get the server's DNS entry */
     80	serveraddr_len = sizeof(serveraddr);
     81	*getting_host_by_name = 1;
     82	serverresolve = resolveName((struct sockaddr*) &serveraddr, &serveraddr_len, hostname, AF_UNSPEC);
     83	*getting_host_by_name = 0;
     84
     85	if (serverresolve != 1) {
     86		ERRMB("ERROR, no such host as \"%s\"\n", hostname);
     87		goto fail;
     88	}
     89
     90	/* build the server's Internet address */
     91	serveraddr.sin6_port = htons(portno);
     92
     93reconnect:
     94	*socketptr = httpsock = socket(serveraddr.sin6_family, SOCK_STREAM, 0);
     95	if (httpsock < 0)
     96	{
     97		ERRMB( "Error opening socket\n" );
     98		return;
     99	}
    100
    101	int sockVal = 1;
    102	// using char* for sockVal for windows
    103	if (setsockopt(httpsock, SOL_TCP, TCP_NODELAY, (char*) &sockVal, 4) != 0)
    104	{
    105		ERRM( "Error: Failed to set TCP_NODELAY\n");
    106		// not a critical error, we can continue
    107	}
    108
    109#if !defined( WIN32 ) && !defined( WINDOWS )
    110	if(device)
    111	{
    112		if( setsockopt(httpsock, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device) +1) != 0)
    113		{
    114			ERRM("Error: Failed to set Device option.\n");
    115			exit( -1 );
    116		}
    117	}
    118#endif // not windows
    119
    120	/* connect: create a connection with the server */
    121	if (connect(httpsock, (struct sockaddr*)&serveraddr, serveraddr_len) < 0)
    122	{
    123		ERRMB( "%s: ERROR connecting: %s (%d)\n", hostname, strerror(errno), errno );
    124		goto fail;
    125	}
    126
    127#ifdef __APPLE__
    128	int opt = 1;
    129	setsockopt(httpsock, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt));
    130#endif
    131
    132	errbuffer[0] = 0;
    133
    134	while( 1 )
    135	{
    136		char buf[8192];
    137
    138		int n = sprintf( buf, "HEAD %s HTTP/1.1\r\nConnection: keep-alive\r\nHost: %s\r\n\r\n", eurl?eurl:"/favicon.ico", hostname );
    139
    140		(*seqnoptr) ++;
    141		HTTPingCallbackStart( *seqnoptr );
    142
    143		int rs = send( httpsock, buf, n, MSG_NOSIGNAL );
    144		double starttime = *timeouttime = OGGetAbsoluteTime();
    145		int breakout = 0;
    146		if( rs != n ) breakout = 1;
    147		int endstate = 0;
    148		while( !breakout )
    149		{
    150#ifdef WIN32
    151			n = recv( httpsock, buf, sizeof(buf)-1, 0);
    152#else
    153			n = recv( httpsock, buf, sizeof(buf)-1, MSG_PEEK);
    154			if( n > 0 ) n = read( httpsock, buf, sizeof(buf)-1);
    155			else if( n == 0 ) break; //FIN received
    156#endif
    157
    158			
    159			if( n < 0 ) return;
    160			
    161			int i;
    162			for( i = 0; i < n; i++ )
    163			{
    164				char c = buf[i];
    165				switch( endstate )
    166				{
    167				case 0: if( c == '\r' ) endstate++; break;
    168				case 1: if( c == '\n' ) endstate++; else endstate = 0; break;
    169				case 2: if( c == '\r' ) endstate++; else endstate = 0; break;
    170				case 3: if( c == '\n' && i == n-1) breakout = 1; else endstate = 0; break;
    171				}
    172			}
    173		}
    174		*timeouttime = OGGetAbsoluteTime();
    175
    176		HTTPingCallbackGot( *seqnoptr );
    177
    178		double delay_time = minperiod - (*timeouttime - starttime);
    179		if( delay_time > 0 )
    180			usleep( (int)(delay_time * 1000000) );
    181		if( !breakout ) {
    182#ifdef WIN32
    183			closesocket( httpsock );
    184#else
    185			close( httpsock );
    186#endif
    187			goto reconnect;
    188		}
    189	}
    190fail:
    191	return;
    192}
    193
    194
    195struct HTTPPingLaunch
    196{
    197	const char * addy;
    198	double minperiod;
    199	const char * device;
    200
    201	volatile double timeout_time;
    202	volatile int failed;
    203	int seqno;
    204	int socket;
    205	volatile int getting_host_by_name;
    206};
    207
    208static void * DeployPing( void * v )
    209{
    210	struct HTTPPingLaunch *hpl = (struct HTTPPingLaunch*)v;
    211	hpl->socket = 0;
    212	hpl->getting_host_by_name = 0;
    213	DoHTTPing( hpl->addy, hpl->minperiod, &hpl->seqno, &hpl->timeout_time, &hpl->socket, &hpl->getting_host_by_name, hpl->device );
    214	hpl->failed = 1;
    215	return 0;
    216}
    217
    218
    219static void * PingRunner( void * v )
    220{
    221	struct HTTPPingLaunch *hpl = (struct HTTPPingLaunch*)v;
    222	hpl->seqno = 0;
    223	while( 1 )
    224	{
    225		hpl->timeout_time = OGGetAbsoluteTime();
    226		og_thread_t thd = OGCreateThread( DeployPing, hpl );
    227		while( 1 )
    228		{
    229			double Now = OGGetAbsoluteTime();
    230			double usl = hpl->timeout_time - Now + HTTPTIMEOUT;
    231			if( usl > 0 ) usleep( (int)(usl*1000000 + 1000));
    232			else usleep( 10000 );
    233
    234			if( hpl->timeout_time + HTTPTIMEOUT < Now && !hpl->getting_host_by_name ) //Can't terminate in the middle of a gethostbyname operation otherwise bad things can happen.
    235			{
    236				if( hpl->socket )
    237				{
    238#ifdef WIN32
    239					closesocket( hpl->socket );
    240#else
    241					close( hpl->socket );
    242#endif
    243				}
    244
    245				OGCancelThread( thd );
    246				break;
    247			}
    248		}
    249	}
    250	return 0;
    251}
    252
    253int StartHTTPing( const char * addy, double minperiod, const char * device)
    254{
    255	struct HTTPPingLaunch *hpl = malloc( sizeof( struct HTTPPingLaunch ) );
    256	hpl->addy = addy;
    257	hpl->minperiod = minperiod;
    258	hpl->device = device;
    259	OGCreateThread( PingRunner, hpl );
    260	return 0;
    261}
    262
    263
    264