Repository: cnlohr/cnping
Branch: master
Commit: ce49c2d85240
Files: 31
Total size: 63.9 KB
Directory structure:
gitextract_6s2_y85m/
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ └── build.yml
├── .gitignore
├── .gitmodules
├── .travis.yml
├── LICENSE
├── LICENSE.BSD-3-Clause
├── LICENSE.MIT
├── Makefile
├── README.md
├── cnping.1
├── cnping.c
├── debian/
│ ├── changelog
│ ├── control
│ ├── copyright
│ ├── install
│ ├── postinst
│ ├── rules
│ └── source/
│ └── format
├── error_handling.h
├── freedesktop/
│ ├── com.github.cntools.cnping.desktop
│ └── com.github.cntools.cnping.metainfo.xml
├── httping.c
├── httping.h
├── ping.c
├── ping.h
├── resolve.c
├── resolve.h
├── resources.rc
├── searchnet.c
└── uac.manifest
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
================================================
FILE: .github/workflows/build.yml
================================================
name: Build cnping
on:
push:
pull_request:
workflow_dispatch:
jobs:
Build-for-Linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
debhelper-compat \
libglvnd-dev \
libx11-dev \
libxext-dev \
libxinerama-dev \
mesa-common-dev \
make \
build-essential \
binutils-mingw-w64-i686 \
gcc-mingw-w64-i686 \
g++-mingw-w64-i686 \
devscripts \
appstream \
imagemagick \
desktop-file-utils
- name: Build
run: make cnping cnping.exe
- uses: actions/upload-artifact@v7
with:
name: cnping-exe
path: "*.exe"
- name: Build deb
run: debuild --no-sign
- uses: actions/upload-artifact@v7
with:
name: cnping-debian
path: |
/home/runner/work/cnping/*.deb
/home/runner/work/cnping/*.ddeb
- name: Validate AppStream data
run: appstreamcli validate freedesktop/com.github.cntools.cnping.metainfo.xml
- name: Validate desktop file
run: desktop-file-validate freedesktop/com.github.cntools.cnping.desktop
# Build-RPM-for-Fedora:
# runs-on: ubuntu-latest
# container: dreua/ ...
# steps:
# - name: RPM Build
# run: /cnping/cnping-build
# - uses: actions/upload-artifact@v7
# with:
# name: cnping-fedora-rpm
# path: /github/home/rpmbuild/**/*.rpm
================================================
FILE: .gitignore
================================================
*.o
*.exe
cnping
cnping-mousey
searchnet
tags
debian/.debhelper
debian/cnping
debian/cnping.substvars
debian/debhelper-build-stamp
debian/files
================================================
FILE: .gitmodules
================================================
[submodule "rawdraw"]
path = rawdraw
url = https://github.com/cntools/rawdraw
================================================
FILE: .travis.yml
================================================
language: c
dist: bionic
before_install:
- sudo apt-get -qq update
- sudo apt-get install -y make libxinerama-dev libxext-dev libx11-dev build-essential
binutils-mingw-w64-i686 gcc-mingw-w64-i686 g++-mingw-w64-i686 mesa-common-dev libglvnd-dev
script: make cnping cnping.exe
deploy:
provider: releases
api_key:
secure: "iGexfXEEUlID8jiIFaly4RcWBS1iFiT2jTtwLEExz0tBMxZuvpvS+MfZMtXt3YTI1NsbY/B/1tfO2CueEfIWW/d8mmCxf1E/VerfxA1j0NgRc3br6L1nFxbkwEiHtsHO5sYf4QGlqgzipfW2u/cp4nO8pme4jGStMTSZxyTSJPAuWT+4qiyDeay4QGLkigplW9570xKVgejX0hBm7zuw7R8QdnRvIftyvvbQiRqnLoYPPDc1PSOxLfdfNTAoUikbMuOkiN1XI3s3mytTYKCiUa3Zxj1BS35AEPCxaQoOr0iyY+jBFZrwdvPOTAD8VsAGRzjvQ0NldnuIZlXJnFAvtwjxRIIS2rPaLqC79lphaw2j0564tDY0p+j1MhVBt1wjAkRg/Dh2R1SzcO3baxJH6MHKCGS1lUxkUgW0kBoKaIw/DzfnXHx5EsJPbyLeksyLf8XzYCTPMaVJEQK30b0TM5OOEcwCrtAjwX9IiIXAgZiGyw7E4CuKWU+RfZRee8IvhF0qs5DIn0yiCmvYIMYZvX9rL6mm1Wn1Hb7+o4bxHzMmuFmo9HJfgYOzoIhBmr3Q1DIbH/UHcqeFWrP/Dz47easv3GRSbnXcbyh8G0d7mFheO+9JdAnoGBzr1Ig5Eeq0Q1ppjP7jCQuPJQ9EP3uJHStQlIVJse95PZD+TyP8LW4="
file:
- cnping.exe
skip_cleanup: true
on:
tags: true
repo: cntools/cnping
branch: master
draft: true
================================================
FILE: LICENSE
================================================
//Rawdraw and CNPing may be licensed feely under the MIT-x11
//or NewBSD Licenses. You choose!
================================================
FILE: LICENSE.BSD-3-Clause
================================================
BSD 3-Clause License
Copyright (c) 2018 Charles Lohr
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
(1) Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
(3)The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: LICENSE.MIT
================================================
MIT License
Copyright (c) 2018 Charles Lohr
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Makefile
================================================
CFLAGS ?= -s -Os -I/opt/X11/include -Wall
LDFLAGS ?= -s -L/opt/X11/lib/
CC? = gcc
ICONSPATH = freedesktop/icons/hicolor/
APPNAME = com.github.cntools.cnping
all : cnping
#CFLAGS := $(CFLAGS) -DCNFGOGL
#LDFLAGS := $(LDFLAGS) -lGL
clean :
rm -rf *.o *~ cnping cnping.exe cnping_mac searchnet
rm -rf rawdraw/*.o
# Windows
#MINGW32 := /usr/bin/i686-w64-mingw32-
MINGW32 ?= i686-w64-mingw32-
# If you don't need admin privileges
ADMINFLAGS := $(ADMINFLAGS) -DWIN_USE_NO_ADMIN_PING
# Add git version to CFLAGS
GIT_VERSION := "$(shell git describe --abbrev=4 --dirty --always --tags)"
CFLAGS += -DVERSION=\"$(GIT_VERSION)\"
cnping-wingdi.exe : cnping.c ping.c httping.c resources.o
$(MINGW32)gcc -g -fno-ident -mwindows -m32 $(CFLAGS) -o $@ $^ -lgdi32 -lws2_32 -s -D_WIN32_WINNT=0x0600 -DWIN32 -liphlpapi -DMINGW_BUILD $(ADMINFLAGS)
cnping.exe : cnping.c ping.c httping.c resolve.c resources.o
$(MINGW32)gcc -g -fno-ident -mwindows -m32 -DCNFGOGL $(CFLAGS) -o $@ $^ -lgdi32 -lws2_32 -s -D_WIN32_WINNT=0x0600 -DWIN32 -liphlpapi -lopengl32 -DMINGW_BUILD $(ADMINFLAGS)
resources.o : resources.rc
$(MINGW32)windres resources.rc -o resources.o $(ADMINFLAGS)
# Unix
cnping : cnping.c ping.c httping.c resolve.c
$(CC) $(CFLAGS) -o $@ $^ -lX11 -lm -lpthread -lGL $(LDFLAGS)
cnping_ogl : cnping.c ping.c httping.c resolve.c
$(CC) $(CFLAGS) -o $@ $^ -DCNFGOGL $(CFLAGS) -lX11 -lm -lpthread $(LDFLAGS) -lGL
cnping_mac : cnping.c ping.c httping.c resolve.c
$(CC) -o cnping $^ -x objective-c -framework Cocoa -framework QuartzCore -lm -lpthread -DOSX
searchnet : ping.c searchnet.c resolve.c
$(CC) $(CFLAGS) -o $@ $^ -lpthread
linuxinstall : cnping
rm -f /usr/local/bin/cnping
cp cnping /usr/local/bin/
cp -r $(ICONSPATH) /usr/local/share/icons
cp freedesktop/${APPNAME}.desktop /usr/local/share/applications
cp freedesktop/${APPNAME}.metainfo.xml /usr/local/share/metainfo
setcap cap_net_raw+ep /usr/local/bin/cnping
# sudo chmod +t /usr/local/bin/cnping #One option - set the stuid bit.
# sudo install cnping /usr/local/bin/ #Another option - using install.
# minimal linux install, may be useful for development
minlinuxinstall : cnping
sudo rm -f /usr/local/bin/cnping
sudo cp cnping /usr/local/bin/
sudo setcap cap_net_raw+ep /usr/local/bin/cnping
# this target requires imagemagick
updateicons : ${ICONSPATH}scalable/apps/${APPNAME}.svg
convert $^ -resize 16x16 ${ICONSPATH}16x16/apps/${APPNAME}.png
convert $^ -resize 32x32 ${ICONSPATH}32x32/apps/${APPNAME}.png
convert $^ -resize 48x48 ${ICONSPATH}48x48/apps/${APPNAME}.png
convert $^ -resize 256x256 ${ICONSPATH}256x256/apps/${APPNAME}.png
convert $^ -resize 1024x1024 ${ICONSPATH}1024x1024/apps/${APPNAME}.png
# after creating the ico file use GIMP to compress it:
# Image-> Mode -> Indexed...
# Choose "Generate optimum palette"
# Maximum number of colors: 3 (may change if the icon changes)
# "Convert"
# File -> Export As
# Check "Compressed (PNG)" in every resolution
# "Export"
cnping.ico: ${ICONSPATH}scalable/apps/${APPNAME}.svg
convert $^ -density 300 -define icon:auto-resize=256,64,16 -background none $@
================================================
FILE: README.md
================================================
cnping
======
Minimal Graphical IPV4 Ping/HTTP Ping Tool. (also comes with searchnet, like nmap but smaller and simpler). It uses rawdraw so it is OS independent.
```
Usage: cnping [host] [period] [extra size] [y-axis scaling] [window title]
[host] -- domain or IP address of ICMP ping target, or http://[host] i.e. http://google.com
[period] -- period in seconds (optional), default 0.02
[extra size] -- ping packet extra size (above 12), optional, default = 0
[const y-axis scaling] -- use a fixed scaling factor instead of auto scaling (optional)
[window title] -- the title of the window (optional)
```
<IMG SRC=https://i.imgur.com/Yj5coKN.gif>
If an http host is listed, the default request is ```HEAD /favicon.ico HTTP/1.1``` since this is usually a very fast, easy operation for the server. If a specific file or uri is requested, that will be requested instead, i.e. http://github.com/cnlohr will request ```HEAD /cnlohr HTTP/1.1```.
If a regular hostname is requested instead, ICMP (regular ping) will be used.
This allows cnping to be operated in environments where ICMP is prohibited by local computer or network policies.
## Installation
### Ubuntu
```
sudo apt install libxinerama-dev libxext-dev libx11-dev build-essential mesa-common-dev libglvnd-dev
git clone https://github.com/cntools/cnping.git
cd cnping/
git submodule update --init --recursive
make linuxinstall
```
'linuxinstall' builds the tool, copies it to your /usr/local/bin folder, and sets the cap_net_raw capability allowing it to create raw sockets without being root.
```
sudo cp cnping /usr/local/bin/
sudo setcap cap_net_raw+ep /usr/local/bin/cnping
```
Note that if only http pinging is requested, you do not need cap_net_raw or root access.
### Windows
A Windows-exe can be cross compiled on Linux, just install the necessary dependencies and compile it:
```
sudo apt install binutils-mingw-w64-i686 gcc-mingw-w64-i686 g++-mingw-w64-i686
make cnping.exe
```
### Other distributions
See our Wiki page for information on more distributions, e.g. Arch, Void, Gentoo or Fedora: [Binary packages and distributions](https://github.com/cntools/cnping/wiki/Binary-packages-and-distributions)
================================================
FILE: cnping.1
================================================
.TH CNPING 1 "Dec 2020" "version 1.0" "User Manuals"
.SH "NAME"
cnping \- Minimal Graphical IPv4 Ping/HTTP Ping Tool
.SH "SYNOPSIS"
.B cnping cnping [host] [period] [extra size] [y-axis scaling] [window title]
.SH "DESCRIPTION"
.B cnping
is a minimal graphical real time IPv4 ping tool written in C.
It can send pings via ICMP (regular ping) or HTTP which is useful
in case ICMP is prohibited. Responses are displayed as vertical bars
in a graphical window. Red bars indicate a response was not (yet)
received, white bars indicate the response was received with a height
relative to the round trip time. Additional statistics are displayed
as an overlay.
.SH "OPTIONS"
All options can be passed either positional or as flags.
.IP -h
domain, IP address of ping target for ICMP or http host, i.e. http://google.com
.IP -p
period in seconds (optional), default 0.02
.IP -s
ping packet extra size (above 12), optional, default = 0
.IP -y
use a fixed scaling factor instead of auto scaling (optional)
.IP -t
the title of the window (optional)
.SH "GUI"
.IP q
quit
.IP m
toggle histogram mode
.IP f
toggle histogram frame mode
.IP c
reset histogram data
.SH "AUTHOR"
Charles Lohr <TODO Email>
.SH COPYRIGHT
Copyright \(co 2018-2020 Charles Lohr
.br
.SH LICENSE
Rawdraw and CNPing may be licensed feely under the MIT-x11
or NewBSD Licenses. You choose!
================================================
FILE: cnping.c
================================================
//Copyright (c) 2011-2019 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <errno.h>
#include <string.h>
#if defined( WINDOWS ) || defined( WIN32 )
#ifdef _MSC_VER
#define strdup _strdup
#endif
#include <windows.h>
#else
#ifdef __FreeBSD__
#include <sys/types.h>
#include <netinet/in.h>
#endif
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <netdb.h>
#endif
#ifndef CNFGOGL
#define CNFGOGL
#endif
#define CNFG_IMPLEMENTATION
#include "rawdraw/os_generic.h"
#include "rawdraw/CNFG.h"
#include "ping.h"
#include "error_handling.h"
#include "httping.h"
// #### Cross-plattform debugging ####
// Windows does not print to Console, use DebugView from SysInternals to
// see the output. (Setup: Computer -> Connect Local; Capture -> Capture Win32)
// Warning: Debugging on Windows can slow cnping down and lead to wrong measurements!
//#define DEBUG
#ifdef DEBUG
char msgbuf[1024];
#ifdef WIN32
#define debug(...) \
snprintf(msgbuf, sizeof(msgbuf), __VA_ARGS__); \
OutputDebugString(msgbuf);
#else
#define debug(...) printf(__VA_ARGS__);
#endif
#else
// Let the compiler parse it to catch errors. Compiler will optimize away.
#define debug(...) \
do { if (0) fprintf(stderr, __VA_ARGS__); } while (0);
#endif
unsigned frames = 0;
unsigned long iframeno = 0;
short screenx, screeny;
const char * pinghost;
float GuiYScaleFactor;
int GuiYscaleFactorIsConstant;
double globmaxtime, globmintime = 1e20;
double globinterval, globlast;
uint64_t globalrx;
uint64_t globallost;
// Ping Data. Will be overwritten with random bytes when !DEBUG
uint8_t pattern[8] = {0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF};
#define PINGCYCLEWIDTH 8192
#define TIMEOUT 4
double PingSendTimes[PINGCYCLEWIDTH];
double PingRecvTimes[PINGCYCLEWIDTH];
int current_cycle = 0;
int ExtraPingSize;
int in_histogram_mode, in_frame_mode = 1;
void HandleGotPacket( int seqno, int timeout );
#if defined( WINDOWS ) || defined( WIN32 )
WSADATA wsaData;
#endif
#define MAX_HISTO_MARKS (TIMEOUT*10000)
uint64_t hist_counts[MAX_HISTO_MARKS];
void HandleNewPacket( int seqno )
{
double Now = OGGetAbsoluteTime();
PingSendTimes[seqno] = Now;
PingRecvTimes[seqno] = 0;
static int timeoutmark;
while( Now - PingSendTimes[timeoutmark] > TIMEOUT )
{
if( PingRecvTimes[timeoutmark] < PingSendTimes[timeoutmark] )
{
HandleGotPacket( timeoutmark, 1 );
}
timeoutmark++;
if( timeoutmark >= PINGCYCLEWIDTH ) timeoutmark = 0;
}
}
void HandleGotPacket( int seqno, int timeout )
{
double Now = OGGetAbsoluteTime();
if( timeout )
{
if( PingRecvTimes[seqno] < -0.5 ) return;
globallost++;
PingRecvTimes[seqno] = -1;
hist_counts[MAX_HISTO_MARKS-1]++;
return;
}
if( PingRecvTimes[seqno] >= PingSendTimes[seqno] ) return;
if( PingSendTimes[seqno] < 1 ) return;
if( Now - PingSendTimes[seqno] > TIMEOUT ) return;
PingRecvTimes[seqno] = OGGetAbsoluteTime();
double Delta = PingRecvTimes[seqno] - PingSendTimes[seqno];
if( Delta > globmaxtime ) { globmaxtime = Delta; }
if( Delta < globmintime ) { globmintime = Delta; }
int slot = Delta * 10000;
if( slot >= MAX_HISTO_MARKS ) slot = MAX_HISTO_MARKS-1;
if( slot < 0 ) slot = 0;
hist_counts[slot]++;
if( globlast > 0.5 )
{
if( Now - globlast > globinterval ) globinterval = Now - globlast;
}
globlast = Now;
globalrx++;
}
void HTTPingCallbackStart( int seqno )
{
current_cycle = seqno;
HandleNewPacket( seqno );
}
void HTTPingCallbackGot( int seqno )
{
HandleGotPacket( seqno, 0 );
}
void display(uint8_t *buf, int bytes)
{
int reqid = (buf[0] << 24) | (buf[1]<<16) | (buf[2]<<8) | (buf[3]);
debug("Received ping: reqid=%d\n", reqid);
reqid &= (PINGCYCLEWIDTH-1);
if( memcmp( buf+4, pattern, sizeof(pattern) ) != 0 ) return;
debug("Memcmp OK, checked %ld bytes, first values being %x %x %x %x\n",
(long int) sizeof(pattern), pattern[0], pattern[1], pattern[2], pattern[3])
HandleGotPacket( reqid, 0 );
}
int load_ping_packet( uint8_t * buffer, int bufflen )
{
buffer[0] = current_cycle >> 24;
buffer[1] = current_cycle >> 16;
buffer[2] = current_cycle >> 8;
buffer[3] = current_cycle >> 0;
memcpy( buffer+4, pattern, sizeof(pattern) );
if( ping_failed_to_send )
{
PingSendTimes[(current_cycle+PINGCYCLEWIDTH-1)&(PINGCYCLEWIDTH-1)] = 0; //Unset ping send.
}
HandleNewPacket( current_cycle&(PINGCYCLEWIDTH-1) );
current_cycle++;
return 12 + ExtraPingSize;
}
void * PingListen( void * r )
{
listener();
ERRM( "Fault on listen.\n" );
exit( -2 );
}
void * PingSend( void * r )
{
do_pinger( );
ERRM( "Fault on ping.\n" );
exit( -1 );
}
void HandleKey( int keycode, int bDown )
{
switch( keycode )
{
#if defined( WIN32 ) || defined( WINDOWS )
case 'r':
{
char lpFilename[1024];
char lpDirectory[1024];
GetCurrentDirectory( 1023, lpDirectory );
GetModuleFileNameA( GetModuleHandle(0), lpFilename, 1023 );
CreateProcessA( lpFilename, GetCommandLine(), 0, 0, 1, 0, 0, lpDirectory, 0, 0 );
exit( 0 );
break;
}
#endif
case 'f':
if( bDown ) in_frame_mode = !in_frame_mode;
if( !in_frame_mode ) in_histogram_mode = 1;
break;
case 'm':
if( bDown ) in_histogram_mode = !in_histogram_mode;
if( !in_histogram_mode ) in_frame_mode = 1;
break;
case 'c':
memset( hist_counts, 0, sizeof( hist_counts ) );
globmaxtime = 0;
globmintime = 1e20;
globinterval = 0;
globlast = 0;
globalrx = 0;
globallost = 0;
break;
case 'q':
exit(0);
break;
}
}
void HandleButton( int x, int y, int button, int bDown ){}
void HandleMotion( int x, int y, int mask ){}
void HandleDestroy() { exit(0); }
double GetWindMaxPingTime( void )
{
int i;
double maxtime = 0;
for( i = 0; i < screenx; i++ )
{
int index = ((current_cycle - i - 1) + PINGCYCLEWIDTH) & (PINGCYCLEWIDTH-1);
double st = PingSendTimes[index];
double rt = PingRecvTimes[index];
double dt = 0;
if( rt > st )
{
dt = rt - st;
dt *= 1000;
if( dt > maxtime ) maxtime = dt;
}
}
return maxtime;
}
void DrawMainText( const char * stbuf )
{
int x, y;
CNFGColor( 0x000000ff );
for( x = -1; x < 2; x++ ) for( y = -1; y < 2; y++ )
{
CNFGPenX = 10+x; CNFGPenY = 10+y;
CNFGDrawText( stbuf, 2 );
}
CNFGColor( 0xffffffff );
CNFGPenX = 10; CNFGPenY = 10;
CNFGDrawText( stbuf, 2 );
}
void DrawFrameHistogram()
{
int i;
// double Now = OGGetAbsoluteTime();
const int colwid = 50;
int categories = (screenx-50)/colwid;
int maxpingslot = ( globmaxtime*10000.0 );
int minpingslot = ( globmintime*10000.0 );
int slots = maxpingslot-minpingslot;
if( categories <= 2 )
{
goto nodata;
}
else
{
int skips = ( (slots)/categories ) + 1;
int slotsmax = maxpingslot / skips + 1;
int slotsmin = minpingslot / skips;
slots = slotsmax - slotsmin;
if( slots <= 0 ) goto nodata;
uint64_t samples[slots+2];
int ssmsMIN[slots+2];
int ssmsMAX[slots+2];
int samp = minpingslot - 1;
if( slots <= 1 ) goto nodata;
memset( samples, 0, sizeof( samples ) );
if( samp < 0 ) samp = 0;
uint64_t highestchart = 0;
int tslot = 0;
for( i = slotsmin; i <= slotsmax; i++ )
{
int j;
uint64_t total = 0;
ssmsMIN[tslot] = samp;
for( j = 0; j < skips; j++ )
{
total += hist_counts[samp++];
}
ssmsMAX[tslot] = samp;
if( total > highestchart ) highestchart = total;
samples[tslot++] = total;
}
if( highestchart <= 0 )
{
goto nodata;
}
int rslots = 0;
for( i = 0; i < slots+1; i++ )
{
if( samples[i] ) rslots = i;
}
rslots++;
for( i = 0; i < rslots; i++ )
{
CNFGColor( 0x33cc33ff );
int top = 30;
uint64_t samps = samples[i];
int bottom = screeny - 50;
int height = samps?(samps * (bottom-top) / highestchart + 1):0;
int startx = (i+1) * (screenx-50) / rslots;
int endx = (i+2) * (screenx-50) / rslots;
if( !in_frame_mode )
{
CNFGTackRectangle( startx, bottom-height, endx, bottom + 1 );
CNFGColor( 0x000000ff );
}
else
{
CNFGColor( 0x8080ffff );
}
CNFGTackSegment( startx, bottom+1, endx, bottom+1 );
CNFGTackSegment( startx, bottom-height, startx, bottom+1 );
CNFGTackSegment( endx, bottom-height, endx, bottom+1 );
CNFGTackSegment( startx, bottom-height, endx, bottom-height );
char stbuf[1024];
int log10 = 1;
int64_t ll = samps;
while( ll >= 10 )
{
ll /= 10;
log10++;
}
if( !in_frame_mode )
{
CNFGColor( 0xffffffff );
}
else
{
CNFGColor( 0x8080ffff );
}
CNFGPenX = startx + (8-log10) * 4; CNFGPenY = bottom+3;
#ifdef WIN32
sprintf( stbuf, "%I64u", samps );
#else
sprintf( stbuf, "%lu", samps );
#endif
CNFGDrawText( stbuf, 2 );
CNFGPenX = startx; CNFGPenY = bottom+14;
sprintf( stbuf, "%5.1fms\n%5.1fms", ssmsMIN[i]/10.0, ssmsMAX[i]/10.0 );
CNFGDrawText( stbuf, 2 );
}
char stt[1024];
#ifdef WIN32
snprintf( stt, 1024, "Host: %s\nHistorical max %9.2fms\nBiggest interval%9.2fms\nHistorical packet loss %I64u/%I64u = %6.3f%%",
#else
snprintf( stt, 1024, "Host: %s\nHistorical max %9.2fms\nBiggest interval%9.2fms\nHistorical packet loss %lu/%lu = %6.3f%%",
#endif
pinghost, globmaxtime*1000.0, globinterval*1000.0, globallost, globalrx, globallost*100.0/(globalrx+globallost) );
if( !in_frame_mode )
DrawMainText( stt );
return;
}
nodata:
DrawMainText( "No data.\n" );
return;
}
void DrawFrame( void )
{
int i;
double now = OGGetAbsoluteTime();
double totaltime = 0;
int totalcountok = 0;
int totalcountloss = 0;
double mintime = 10000;
double maxtime = 0;
double stddev = 0;
double last = -1;
double loss = 100.00;
double windmaxtime = GetWindMaxPingTime();
for( i = 0; i < screenx; i++ )
{
int index = ((current_cycle - i - 1) + PINGCYCLEWIDTH) & (PINGCYCLEWIDTH-1);
double st = PingSendTimes[index];
double rt = PingRecvTimes[index];
double dt = 0;
if( rt > st ) // ping received
{
CNFGColor( 0xffffffff );
dt = rt - st;
dt *= 1000;
totaltime += dt;
if( dt < mintime ) mintime = dt;
if( dt > maxtime ) maxtime = dt;
totalcountok++;
if( last < 0)
last = dt;
}
else if (st != 0) // ping sent but not received
{
CNFGColor( 0xff0000ff );
dt = now - st;
dt *= 1000;
if( i > 5 ) totalcountloss++; //Get a freebie on the first 5.
}
else // no ping sent for this point in time (after startup)
{
CNFGColor( 0x000000ff );
dt = 99 * 1000; // assume 99s to fill screen black
}
if (!GuiYscaleFactorIsConstant)
{
GuiYScaleFactor = (screeny - 50) / windmaxtime;
}
int h = dt*GuiYScaleFactor;
int top = screeny - h;
if( top < 0 ) top = 0;
CNFGTackSegment( i, screeny-1, i, top );
}
double avg = totaltime / totalcountok;
loss = (double) totalcountloss / (totalcountok + totalcountloss) * 100;
for( i = 0; i < screenx; i++ )
{
int index = ((current_cycle - i - 1) + PINGCYCLEWIDTH) & (PINGCYCLEWIDTH-1);
double st = PingSendTimes[index];
double rt = PingRecvTimes[index];
double dt = 0;
if( rt > st )
{
dt = rt - st;
dt *= 1000;
stddev += (dt-avg)*(dt-avg);
}
}
stddev /= totalcountok;
stddev = sqrt(stddev);
int avg_gui = avg*GuiYScaleFactor;
int stddev_gui = stddev*GuiYScaleFactor;
CNFGColor( 0x00ff00ff );
int l = avg_gui;
CNFGTackSegment( 0, screeny-l, screenx, screeny - l );
l = (avg_gui) + (stddev_gui);
CNFGTackSegment( 0, screeny-l, screenx, screeny - l );
l = (avg_gui) - (stddev_gui);
CNFGTackSegment( 0, screeny-l, screenx, screeny - l );
char stbuf[2048];
char * sptr = &stbuf[0];
sptr += sprintf( sptr,
"Last:%6.2f ms Host: %s\n"
"Min :%6.2f ms\n"
"Max :%6.2f ms Historical max: %5.2f ms\n"
"Avg :%6.2f ms Biggest interval: %5.2f ms\n"
#ifdef WIN32
"Std :%6.2f ms Historical loss: %I64u/%I64u %5.3f%%\n"
#else
"Std :%6.2f ms Historical loss: %lu/%lu %5.3f%%\n"
#endif
"Loss:%6.1f %%", last, pinghost, mintime, maxtime, globmaxtime*1000, avg, globinterval*1000.0, stddev,
globallost, globalrx+globallost, globallost*100.0f/(globalrx+globallost), loss );
DrawMainText( stbuf );
OGUSleep( 1000 );
}
#ifdef WIN32
const char * glargv[10];
int glargc = 0;
int RegString( int write, char * data, DWORD len )
{
HKEY hKey;
if( RegCreateKeyExA(HKEY_CURRENT_USER, "Software\\cnping", 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL) == ERROR_SUCCESS)
{
if( write )
{
RegSetValueExA( hKey, "history", 0, REG_SZ, (uint8_t*)data, len );
return 0;
}
else
{
DWORD type;
if( RegGetValueA( hKey, "", "history", 0x02, &type, data, &len ) == ERROR_SUCCESS )
{
return 0;
}
return -16;
}
RegCloseKey( hKey );
}
else
{
return -15;
}
}
INT_PTR CALLBACK TextEntry( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch( uMsg )
{
case WM_INITDIALOG:
SetDlgItemText(hwndDlg, 4, "0.02");
SetDlgItemText(hwndDlg, 5, "0" );
char data[1024];
if( !RegString( 0, data, sizeof( data ) ) )
{
SetDlgItemText(hwndDlg, 3, data);
}
return 0;
case WM_COMMAND:
switch( wParam>>24 )
{
case 4: case 3: return 0; //keyboard input
case 1: case 2: return 0; //focus changed.
case 0:
{
int id = wParam & 0xffffff;
if( id == 8 || id == 2 )
{
exit( -1 );
}
char Address[128]; GetDlgItemText(hwndDlg, 3, Address, sizeof(Address));
char Period[128]; GetDlgItemText(hwndDlg, 4, Period, sizeof(Period));
char Extra[128]; GetDlgItemText(hwndDlg, 5, Extra, sizeof(Extra));
char Scaling[128]; GetDlgItemText(hwndDlg, 6, Scaling, sizeof(Scaling));
if( strlen( Address ) )
{
RegString( 1, Address, strlen( Address ) );
glargc = 2;
glargv[1] = strdup( Address );
if( strlen( Period ) )
{
glargc = 3;
glargv[2] = strdup( Period );
if( strlen( Extra ) )
{
glargc = 4;
glargv[3] = strdup( Extra );
if( strlen( Scaling ) )
{
glargc = 5;
glargv[4] = strdup( Scaling );
}
}
}
}
EndDialog(hwndDlg, 0);
return 0; //User pressed enter.
}
}
/* return 0;
case WM_CTLCOLORBTN:
//printf( "ctr %p %p %p\n", uMsg, wParam, lParam );
//return 0;
case 32: case 512: case 132: case 24: case 70:
case 127: case 783: case 28: case 134: case 6: case 7:
case 8: case 312: case 15: case 71: case 133: case 307:
case 20: case 310: case 33:
return 0;
*/
}
return 0;
}
#endif
int main( int argc, const char ** argv )
{
char title[1024];
int i;
double ThisTime;
double LastFPSTime = OGGetAbsoluteTime();
double LastFrameTime = OGGetAbsoluteTime();
double SecToWait;
double frameperiodseconds;
const char * device = NULL;
#ifdef WIN32
ShowWindow (GetConsoleWindow(), SW_HIDE);
#endif
srand( (uintmax_t)(OGGetAbsoluteTime()*100000) );
#ifndef DEBUG
for( i = 0; i < sizeof( pattern ); i++ )
{
pattern[i] = rand();
}
#endif
CNFGBGColor = 0x000080ff;
#ifdef WIN32
if( argc < 2 )
{
DialogBox(0, "IPDialog", 0, TextEntry );
argc = glargc;
argv = glargv;
}
#endif
pingperiodseconds = 0.02;
ExtraPingSize = 0;
title[0] = 0;
GuiYScaleFactor = 0;
//We need to process all the unmarked parameters.
int argcunmarked = 1;
int displayhelp = 0;
for( i = 1; i < argc; i++ )
{
const char * thisargv = argv[i];
if( thisargv[0] == '-' )
{
int np = ++i;
if( np >= argc )
{
displayhelp = 1;
break;
}
const char * nextargv = argv[np];
//Parameter-based field.
switch( thisargv[1] )
{
case 'h': pinghost = nextargv; break;
case 'p': pingperiodseconds = atof( nextargv ); break;
case 's': ExtraPingSize = atoi( nextargv ); break;
case 'y': GuiYScaleFactor = atof( nextargv ); break;
case 't': sprintf(title, "%s", nextargv); break;
case 'm': in_histogram_mode = 1; break;
case 'I': device = nextargv; break;
default: displayhelp = 1; break;
}
}
else
{
//Unmarked fields
switch( argcunmarked++ )
{
case 1: pinghost = thisargv; break;
case 2: pingperiodseconds = atof( thisargv ); break;
case 3: ExtraPingSize = atoi( thisargv ); break;
case 4: GuiYScaleFactor = atof( thisargv ); break;
case 5: sprintf(title, "%s", thisargv ); break;
default: displayhelp = 1;
}
}
}
if( title[0] == 0 )
{
sprintf( title, "%s - cnping "VERSION, pinghost );
}
if( GuiYScaleFactor > 0 )
{
GuiYscaleFactorIsConstant = 1;
}
if( !pinghost )
{
displayhelp = 1;
}
if( displayhelp )
{
ERRM( "cnping "VERSION" Usage: cnping [host] [period] [extra size] [y-axis scaling] [window title]\n"
" (-h) [host] -- domain, IP address of ping target for ICMP or http host, i.e. http://google.com\n"
" (-p) [period] -- period in seconds (optional), default 0.02 \n"
" (-s) [extra size] -- ping packet extra size (above 12), optional, default = 0 \n"
" (-y) [const y-axis scaling] -- use a fixed scaling factor instead of auto scaling (optional)\n"
" (-t) [window title] -- the title of the window (optional)\n"
" (-I) [interface] -- Sets source interface (i.e. eth0)\n");
return -1;
}
#if defined( WIN32 ) || defined( WINDOWS )
if(device)
{
ERRM("Error: Device option is not implemented on your platform. PRs welcome.\n");
exit( -1 );
}
if( WSAStartup(MAKEWORD(2,2), &wsaData) )
{
ERRM( "Fault in WSAStartup\n" );
exit( -2 );
}
CNFGSetup( title, 320, 155 );
#else
CNFGSetupWMClass( title, 320, 155, "cnping", "cnping" );
#endif
if( memcmp( pinghost, "http://", 7 ) == 0 )
{
StartHTTPing( pinghost+7, pingperiodseconds, device );
}
else
{
char* protoEnd = strstr( pinghost, "://" );
if ( protoEnd )
{
int protoSize = protoEnd - pinghost;
char protoBuffer[protoSize + 1];
memcpy( protoBuffer, pinghost, protoSize );
protoBuffer[protoSize] = '\0';
ERRM( "Protocol \"%s\" is not supported\n", protoBuffer );
exit( -1 );
}
ping_setup( pinghost, device );
OGCreateThread( PingSend, 0 );
OGCreateThread( PingListen, 0 );
}
frameperiodseconds = fmin(.2, fmax(.03, pingperiodseconds));
while(1)
{
iframeno++;
CNFGHandleInput();
CNFGClearFrame();
CNFGColor( 0xffffffff );
CNFGGetDimensions( &screenx, &screeny );
if( in_frame_mode )
{
DrawFrame();
}
if( in_histogram_mode )
{
DrawFrameHistogram();
}
CNFGPenX = 100; CNFGPenY = 100;
CNFGColor( 0xff0000ff );
CNFGDrawText( errbuffer, 3 );
frames++;
CNFGSwapBuffers();
ThisTime = OGGetAbsoluteTime();
if( ThisTime > LastFPSTime + 1 )
{
frames = 0;
LastFPSTime+=1;
}
SecToWait = frameperiodseconds - ( ThisTime - LastFrameTime );
LastFrameTime += frameperiodseconds;
//printf("iframeno = %d; SecToWait = %f; pingperiodseconds = %f; frameperiodseconds = %f \n", iframeno, SecToWait, pingperiodseconds, frameperiodseconds);
if( SecToWait > 0 )
OGUSleep( (int)( SecToWait * 1000000 ) );
}
return(0);
}
================================================
FILE: debian/changelog
================================================
cnping (1.0.0) unstable; urgency=medium
* Initial Release.
-- Charles Lohr <lohr85@gmail.com> Sun, 04 Dec 2022 14:05:43 +0100
================================================
FILE: debian/control
================================================
Source: cnping
Section: net
Priority: optional
Maintainer: Charles Lohr <lohr85@gmail.com>
Build-Depends: debhelper-compat (= 12),
libglvnd-dev,
libx11-dev,
libxext-dev,
libxinerama-dev,
mesa-common-dev
Standards-Version: 4.6.1
Homepage: https://github.com/cntools/cnping/
VCS-Browser: https://github.com/cntools/cnping/
VCS-Git: https://github.com/cntools/cnping.git
Package: cnping
Architecture: any
Depends: libcap2-bin, ${misc:Depends}, ${shlibs:Depends}
Description: Minimal Graphical IP Ping Tool
cnping is a minimal graphical real time IP ping tool written in C.
It can send pings via ICMP (regular ping) or HTTP which is useful in case ICMP
is prohibited. Responses are displayed as vertical bars in a graphical window.
Red bars indicate a response was not (yet) received, white bars indicate the
response was received with a height relative to the round trip time.
Additional statistics are displayed as an overlay.
.
cnping uses rawdraw so it is OS independent and very light weight.
================================================
FILE: debian/copyright
================================================
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: cnping
Upstream-Contact: Charles Lohr <lohr85@gmail.com>
Source: https://github.com/cntools/cnping/
Files: *
Copyright: 2022 Charles Lohr <lohr85@gmail.com>
License: BSD-3-Clause or MIT
Files: debian/*
Copyright: 2022 Charles Lohr <lohr85@gmail.com>
License: GPL-3.0+
License: GPL-3.0+
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
.
This package 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 General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
.
On Debian systems, the complete text of the GNU General
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
License: BSD-3-Clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
(1) Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
(3)The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
License: MIT
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: debian/install
================================================
cnping /usr/bin/
freedesktop/com.github.cntools.cnping.desktop /usr/share/applications/
freedesktop/com.github.cntools.cnping.metainfo.xml /usr/share/metainfo/
freedesktop/icons/hicolor /usr/share/icons
================================================
FILE: debian/postinst
================================================
#!/bin/sh
set -e
PROGRAM=$(dpkg-divert --truename /usr/bin/cnping)
if [ "$1" = configure ]; then
# If we have setcap installed, try setting cap_net_raw+ep,
# which allows us to install our binaries without the setuid
# bit.
if command -v setcap > /dev/null; then
if setcap cap_net_raw+ep $PROGRAM; then
chmod u-s $PROGRAM
else
echo "Setcap failed on $PROGRAM, falling back to setuid" >&2
chmod u+s $PROGRAM
fi
else
echo "Setcap is not installed, falling back to setuid" >&2
chmod u+s $PROGRAM
fi
fi
#DEBHELPER#
exit 0
================================================
FILE: debian/rules
================================================
#!/usr/bin/make -f
%:
dh $@
================================================
FILE: debian/source/format
================================================
3.0 (native)
================================================
FILE: error_handling.h
================================================
#ifndef _ERROR_HANDLING
#define _ERROR_HANDLING
extern char errbuffer[1024];
#ifdef WIN32
#ifndef _MSC_VER
#define ERRM(x...) { sprintf( errbuffer, x ); MessageBox( 0, errbuffer, "cnping", 0 ); }
#define ERRMB(x...) { sprintf( errbuffer, x ); }
#else
#define ERRM(...) { sprintf( errbuffer, __VA_ARGS__ ); MessageBox( 0, errbuffer, "cnping", 0 ); }
#define ERRMB(...) { sprintf( errbuffer, __VA_ARGS__ ); }
#endif
#else
#define ERRM(x...) { fprintf( stderr, x ); }
#define ERRMB(x...) { sprintf( errbuffer, x); fprintf( stderr, x ); }
#endif
#endif
================================================
FILE: freedesktop/com.github.cntools.cnping.desktop
================================================
[Desktop Entry]
Version=1.0
Name=cnping
Comment=Minimal Graphical IP Ping/HTTP Ping Tool
Type=Application
Icon=com.github.cntools.cnping
Categories=Network;
Keywords=ping;
StartupWMClass=cnping
# cnping is meant to be started from command line, let's give users a hint.
Exec=bash -c "cnping ; bash"
Terminal=true
================================================
FILE: freedesktop/com.github.cntools.cnping.metainfo.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>com.github.cntools.cnping</id>
<name>cnping</name>
<summary>Minimal Graphical IPv4 Ping/HTTP Ping Tool</summary>
<launchable type="desktop-id">com.github.cntools.cnping.desktop</launchable>
<metadata_license>CC0-1.0</metadata_license>
<content_rating type="oars-1.1"/>
<description>
<p>
cnping is a minimal graphical real time IPv4 ping tool written in C.
It can send pings via ICMP (regular ping) or HTTP which is useful
in case ICMP is prohibited. Responses are displayed as vertical bars
in a graphical window. Red bars indicate a response was not (yet)
received, white bars indicate the response was received with a height
relative to the round trip time. Additional statistics are displayed
as an overlay.
</p>
<p>
cnping uses rawdraw so it is OS independent and very light weight.
</p>
</description>
<url type="homepage">https://github.com/cntools/cnping</url>
<screenshots>
<screenshot type="default">
<image>https://i.imgur.com/Yj5coKN.gif</image>
<caption/>
</screenshot>
</screenshots>
<releases><!-- add new versions on top -->
<release date="2019-10-04" version="1.0.0"/>
</releases>
<project_license>MIT or BSD-3-Clause</project_license>
<url type="bugtracker">https://github.com/cntools/cnping/issues</url>
</component>
================================================
FILE: httping.c
================================================
#include "httping.h"
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "resolve.h"
#ifndef TCC
#include <unistd.h>
#include <sys/types.h>
#else
#include "tccheader.h"
#endif
#if defined( WIN32 ) || defined( WINDOWS )
#ifdef TCC
#else
#include <ws2tcpip.h>
#endif
#define SOL_TCP IPPROTO_TCP
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#endif
#include "rawdraw/os_generic.h"
#include "error_handling.h"
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
#define HTTPTIMEOUT 3.0
//Callbacks (when started/received)
void HTTPingCallbackStart( int seqno );
void HTTPingCallbackGot( int seqno );
//Don't dynamically allocate resources here, since execution may be stopped arbitrarily.
void DoHTTPing( const char * addy, double minperiod, int * seqnoptr, volatile double * timeouttime, int * socketptr, volatile int * getting_host_by_name, const char * device)
{
#if defined(WIN32) || defined(WINDOWS)
(void) device; // option is not available for windows. Suppress unused warning.
WSADATA wsaData;
int r = WSAStartup(MAKEWORD(2,2), &wsaData);
if( r )
{
ERRM( "Fault in WSAStartup\n" );
exit( -2 );
}
#endif
struct sockaddr_in6 serveraddr;
socklen_t serveraddr_len;
int serverresolve;
int httpsock;
int addylen = strlen(addy);
char hostname[addylen+1];
memcpy( hostname, addy, addylen + 1 );
char * eportmarker = strchr( hostname, ':' );
char * eurl = strchr( hostname, '/' );
int portno = 80;
if( eportmarker )
{
portno = atoi( eportmarker+1 );
*eportmarker = 0;
}
else
{
if( eurl )
*eurl = 0;
}
/* gethostbyname: get the server's DNS entry */
serveraddr_len = sizeof(serveraddr);
*getting_host_by_name = 1;
serverresolve = resolveName((struct sockaddr*) &serveraddr, &serveraddr_len, hostname, AF_UNSPEC);
*getting_host_by_name = 0;
if (serverresolve != 1) {
ERRMB("ERROR, no such host as \"%s\"\n", hostname);
goto fail;
}
/* build the server's Internet address */
serveraddr.sin6_port = htons(portno);
reconnect:
*socketptr = httpsock = socket(serveraddr.sin6_family, SOCK_STREAM, 0);
if (httpsock < 0)
{
ERRMB( "Error opening socket\n" );
return;
}
int sockVal = 1;
// using char* for sockVal for windows
if (setsockopt(httpsock, SOL_TCP, TCP_NODELAY, (char*) &sockVal, 4) != 0)
{
ERRM( "Error: Failed to set TCP_NODELAY\n");
// not a critical error, we can continue
}
#if !defined( WIN32 ) && !defined( WINDOWS )
if(device)
{
if( setsockopt(httpsock, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device) +1) != 0)
{
ERRM("Error: Failed to set Device option.\n");
exit( -1 );
}
}
#endif // not windows
/* connect: create a connection with the server */
if (connect(httpsock, (struct sockaddr*)&serveraddr, serveraddr_len) < 0)
{
ERRMB( "%s: ERROR connecting: %s (%d)\n", hostname, strerror(errno), errno );
goto fail;
}
#ifdef __APPLE__
int opt = 1;
setsockopt(httpsock, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt));
#endif
errbuffer[0] = 0;
while( 1 )
{
char buf[8192];
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 );
(*seqnoptr) ++;
HTTPingCallbackStart( *seqnoptr );
int rs = send( httpsock, buf, n, MSG_NOSIGNAL );
double starttime = *timeouttime = OGGetAbsoluteTime();
int breakout = 0;
if( rs != n ) breakout = 1;
int endstate = 0;
while( !breakout )
{
#ifdef WIN32
n = recv( httpsock, buf, sizeof(buf)-1, 0);
#else
n = recv( httpsock, buf, sizeof(buf)-1, MSG_PEEK);
if( n > 0 ) n = read( httpsock, buf, sizeof(buf)-1);
else if( n == 0 ) break; //FIN received
#endif
if( n < 0 ) return;
int i;
for( i = 0; i < n; i++ )
{
char c = buf[i];
switch( endstate )
{
case 0: if( c == '\r' ) endstate++; break;
case 1: if( c == '\n' ) endstate++; else endstate = 0; break;
case 2: if( c == '\r' ) endstate++; else endstate = 0; break;
case 3: if( c == '\n' && i == n-1) breakout = 1; else endstate = 0; break;
}
}
}
*timeouttime = OGGetAbsoluteTime();
HTTPingCallbackGot( *seqnoptr );
double delay_time = minperiod - (*timeouttime - starttime);
if( delay_time > 0 )
usleep( (int)(delay_time * 1000000) );
if( !breakout ) {
#ifdef WIN32
closesocket( httpsock );
#else
close( httpsock );
#endif
goto reconnect;
}
}
fail:
return;
}
struct HTTPPingLaunch
{
const char * addy;
double minperiod;
const char * device;
volatile double timeout_time;
volatile int failed;
int seqno;
int socket;
volatile int getting_host_by_name;
};
static void * DeployPing( void * v )
{
struct HTTPPingLaunch *hpl = (struct HTTPPingLaunch*)v;
hpl->socket = 0;
hpl->getting_host_by_name = 0;
DoHTTPing( hpl->addy, hpl->minperiod, &hpl->seqno, &hpl->timeout_time, &hpl->socket, &hpl->getting_host_by_name, hpl->device );
hpl->failed = 1;
return 0;
}
static void * PingRunner( void * v )
{
struct HTTPPingLaunch *hpl = (struct HTTPPingLaunch*)v;
hpl->seqno = 0;
while( 1 )
{
hpl->timeout_time = OGGetAbsoluteTime();
og_thread_t thd = OGCreateThread( DeployPing, hpl );
while( 1 )
{
double Now = OGGetAbsoluteTime();
double usl = hpl->timeout_time - Now + HTTPTIMEOUT;
if( usl > 0 ) usleep( (int)(usl*1000000 + 1000));
else usleep( 10000 );
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.
{
if( hpl->socket )
{
#ifdef WIN32
closesocket( hpl->socket );
#else
close( hpl->socket );
#endif
}
OGCancelThread( thd );
break;
}
}
}
return 0;
}
int StartHTTPing( const char * addy, double minperiod, const char * device)
{
struct HTTPPingLaunch *hpl = malloc( sizeof( struct HTTPPingLaunch ) );
hpl->addy = addy;
hpl->minperiod = minperiod;
hpl->device = device;
OGCreateThread( PingRunner, hpl );
return 0;
}
================================================
FILE: httping.h
================================================
#ifndef _HTTPING_H
#define _HTTPING_H
//Callbacks (when started/received)
void HTTPingCallbackStart( int seqno );
void HTTPingCallbackGot( int seqno );
//addy should be google.com/blah or something like that. Do not include prefixing http://. Port numbers OK.
int StartHTTPing( const char * addy, double minperiod, const char * device);
#endif
================================================
FILE: ping.c
================================================
//Copyright 2017 <>< C. Lohr, under the MIT/x11 License
//Rewritten from Sean Walton and Macmillan Publishers.
//Most of it was rewritten but the header was never updated.
//Now I finished the job.
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "ping.h"
#include "error_handling.h"
#include "resolve.h"
#ifdef TCC
#include "tccheader.h"
#endif
int ping_failed_to_send;
float pingperiodseconds;
int precise_ping;
struct sockaddr_in6 psaddr;
socklen_t psaddr_len;
int using_regular_ping;
#ifdef WIN_USE_NO_ADMIN_PING
#ifdef TCC
//...
#else
#include <inaddr.h>
#include <ws2tcpip.h>
#include <ipexport.h>
#include <icmpapi.h>
#include <windows.h>
#endif
#include "rawdraw/os_generic.h"
#define MAX_PING_SIZE 16384
#define PINGTHREADS 100
#if !defined( MINGW_BUILD ) && !defined( TCC )
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "iphlpapi.lib")
#endif
static og_sema_t s_disp;
static og_sema_t s_ping;
void ping_setup(const char * strhost, const char * device)
{
// resolve host
psaddr_len = sizeof(psaddr);
if( strhost )
resolveName((struct sockaddr*) &psaddr, &psaddr_len, strhost, AF_INET); // only resolve ipv4 on windows
else
psaddr.sin6_family = AF_INET;
s_disp = OGCreateSema();
s_ping = OGCreateSema();
//This function is executed first.
}
void listener()
{
static uint8_t listth;
if( listth ) return;
listth = 1;
OGUnlockSema( s_disp );
//Normally needs to call display(buf + 28, bytes - 28 ); on successful ping.
//This function is executed as a thread after setup.
//Really, we just use the s_disp semaphore to make sure we only launch disp's at correct times.
while(1) { OGSleep( 100000 ); }
return;
}
static HANDLE pinghandles[PINGTHREADS];
static void * pingerthread( void * v )
{
uint8_t ping_payload[MAX_PING_SIZE];
HANDLE ih = *((HANDLE*)v);
int timeout_ms = pingperiodseconds * (PINGTHREADS-1) * 1000;
while(1)
{
OGLockSema( s_ping );
int rl = load_ping_packet( ping_payload, sizeof( ping_payload ) );
struct repl_t
{
ICMP_ECHO_REPLY rply;
uint8_t err_data[16384];
} repl;
DWORD res = IcmpSendEcho( ih,
((struct sockaddr_in*) &psaddr)->sin_addr.s_addr, ping_payload, rl,
0, &repl, sizeof( repl ),
timeout_ms );
int err;
if( !res ) err = GetLastError();
OGLockSema( s_disp );
if( !res )
{
if( err == 11050 )
{
printf( "GENERAL FAILURE\n" );
}
else
{
printf( "ERROR: %d\n", err );
}
}
if( res )
{
display( repl.rply.Data, rl );
}
OGUnlockSema( s_disp );
}
return 0;
}
void singleping(struct sockaddr *addr, socklen_t addr_len )
{
int i;
(void) addr;
(void) addr_len;
if( psaddr.sin6_family != AF_INET )
{
// ipv6 ICMP Ping is not supported on windows
ERRM( "ERROR: ipv6 ICMP Ping is not supported on windows\n" );
exit( -1 );
}
static int did_init_threads = 0;
if( !did_init_threads )
{
did_init_threads = 1;
//Launch pinger threads
for( i = 0; i < PINGTHREADS; i++ )
{
HANDLE ih = pinghandles[i] = IcmpCreateFile();
if( ih == INVALID_HANDLE_VALUE )
{
ERRM( "Cannot create ICMP thread %d\n", i );
exit( 0 );
}
OGCreateThread( pingerthread, &pinghandles[i] );
}
}
//This function is executed as a thread after setup.
if( i >= PINGTHREADS-1 ) i = 0;
else i++;
OGUnlockSema( s_ping );
}
void ping(struct sockaddr *addr, socklen_t addr_len )
{
int i;
(void) addr;
(void) addr_len;
using_regular_ping = 1;
if( psaddr.sin6_family != AF_INET )
{
// ipv6 ICMP Ping is not supported on windows
ERRM( "ERROR: ipv6 ICMP Ping is not supported on windows\n" );
exit( -1 );
}
//Launch pinger threads
for( i = 0; i < PINGTHREADS; i++ )
{
HANDLE ih = pinghandles[i] = IcmpCreateFile();
if( ih == INVALID_HANDLE_VALUE )
{
ERRM( "Cannot create ICMP thread %d\n", i );
exit( 0 );
}
OGCreateThread( pingerthread, &pinghandles[i] );
}
//This function is executed as a thread after setup.
while(1)
{
if( i >= PINGTHREADS-1 ) i = 0;
else i++;
OGUnlockSema( s_ping );
OGUSleep( (int)(pingperiodseconds * 1000000) );
}
}
#else // ! WIN_USE_NO_ADMIN_PING
//The normal way to do it - only problem is Windows needs admin privs.
#ifdef WIN32
#include <winsock2.h>
#define SOL_IP 0
#define F_SETFL 4
#define ICMP_ECHO 8
#define IP_TTL 2
#define O_NONBLOCK 04000
#ifndef MINGW_BUILD
#pragma comment(lib, "Ws2_32.lib")
#endif
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdint.h>
#else // ! WIN32
#ifdef __FreeBSD__
#include <netinet/in.h>
#endif
#include <unistd.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#if defined(__APPLE__) || defined(__FreeBSD__)
#ifndef SOL_IP
#define SOL_IP IPPROTO_IP
#endif
#endif
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
#endif
#include "rawdraw/os_generic.h"
#if defined WIN32 || defined __APPLE__
struct icmphdr
{
uint8_t type;
uint8_t code;
uint16_t checksum;
union
{
struct
{
uint16_t id;
uint16_t sequence;
} echo;
uint32_t gateway;
struct
{
uint16_t __unused;
uint16_t mtu;
} frag;
} un;
};
#endif
#define PACKETSIZE 65536
struct packet
{
#ifdef __FreeBSD__
struct icmp hdr;
unsigned char msg[PACKETSIZE-sizeof(struct icmp)];
#else
struct icmphdr hdr;
unsigned char msg[PACKETSIZE-sizeof(struct icmphdr)];
#endif
};
int sd;
int pid=-1;
uint16_t checksum( const unsigned char * start, uint16_t len )
{
uint16_t i;
const uint16_t * wptr = (uint16_t*) start;
uint32_t csum = 0;
for (i=1;i<len;i+=2)
csum += (uint32_t)(*(wptr++));
if( len & 1 ) //See if there's an odd number of bytes?
csum += *(uint8_t*)wptr;
if (csum>>16)
csum = (csum & 0xFFFF)+(csum >> 16);
//csum = (csum>>8) | ((csum&0xff)<<8);
return ~csum;
}
// setsockopt TTL to 255
void setTTL(int sock)
{
const int val=255;
assert(psaddr.sin6_family == AF_INET || psaddr.sin6_family == AF_INET6);
if ( setsockopt(sd, (psaddr.sin6_family == AF_INET) ? SOL_IP : SOL_IPV6, IP_TTL, &val, sizeof(val)) != 0)
{
ERRM("Error: Failed to set TTL option. Are you root? Or can do sock_raw sockets?\n");
exit( -1 );
}
}
// 0 = failed, 1 = this is a ICMP Response
int isICMPResponse(unsigned char* buf, int bytes)
{
assert(psaddr.sin6_family == AF_INET || psaddr.sin6_family == AF_INET6);
if( bytes == -1 ) return 0;
if( psaddr.sin6_family == AF_INET ) // ipv4 compare
{
if( buf[9] != IPPROTO_ICMP ) return 0;
if( buf[20] != ICMP_ECHOREPLY ) return 0;
}
else if( psaddr.sin6_family == AF_INET6 ) // ipv6 compare
{
if( buf[0] != ICMP6_ECHO_REPLY ) return 0;
}
return 1;
}
int createSocket()
{
if( psaddr.sin6_family == AF_INET )
{
return socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
}
else if( psaddr.sin6_family == AF_INET6 )
{
return socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
}
// invalid af_family
return -1;
}
void listener()
{
#ifndef WIN32
int sd = createSocket();
setTTL(sd);
#endif
struct sockaddr_in6 addr;
unsigned char buf[66000];
#ifdef WIN32
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
#endif
for (;;)
{
socklen_t addrlenval=sizeof(addr);
int bytes;
#ifdef WIN32
WSAPOLLFD fda[1];
fda[0].fd = sd;
fda[0].events = POLLIN;
WSAPoll(fda, 1, 10);
#endif
keep_retry_quick:
bytes = recvfrom(sd, buf, sizeof(buf), 0, (struct sockaddr*)&addr, &addrlenval );
if( !isICMPResponse(buf, bytes) ) continue;
// compare the sender
if( using_regular_ping && memcmp(&addr, &psaddr, addrlenval) != 0 ) continue;
// sizeof(packet.hdr) + 20
int offset = 0;
if(addr.sin6_family == AF_INET) // ipv4
{
#ifdef __FreeBSD__
offset = 48;
#else
offset = 28;
#endif
}
else // ipv6
{
offset = 8;
}
if ( bytes > 0 )
display(buf + offset, bytes - offset );
else
{
ERRM("Error: recvfrom failed");
}
goto keep_retry_quick;
}
ERRM( "Fault on listen().\n" );
exit( 0 );
}
void singleping(struct sockaddr *addr, socklen_t addr_len )
{
int cnt=1;
#ifdef WIN32
{
//Setup windows socket for nonblocking io.
unsigned long iMode = 1;
ioctlsocket(sd, FIONBIO, &iMode);
}
#else
if ( fcntl(sd, F_SETFL, O_NONBLOCK) != 0 )
ERRM("Warning: Request nonblocking I/O failed.");
#endif
double stime = OGGetAbsoluteTime();
struct packet pckt;
{
int rsize = load_ping_packet( pckt.msg, sizeof( pckt.msg ) );
memset( &pckt.hdr, 0, sizeof( pckt.hdr ) ); //This needs to be here, but I don't know why, since I think the struct is fully populated.
#ifdef __FreeBSD__
pckt.hdr.icmp_code = 0;
pckt.hdr.icmp_type = ICMP_ECHO;
pckt.hdr.icmp_id = pid;
pckt.hdr.icmp_seq = cnt++;
pckt.hdr.icmp_cksum = checksum((const unsigned char *)&pckt, sizeof( pckt.hdr ) + rsize );
#else
pckt.hdr.code = 0;
pckt.hdr.type = (psaddr.sin6_family == AF_INET) ? ICMP_ECHO : ICMP6_ECHO_REQUEST;
pckt.hdr.un.echo.id = pid;
pckt.hdr.un.echo.sequence = cnt++;
pckt.hdr.checksum = checksum((const unsigned char *)&pckt, sizeof( pckt.hdr ) + rsize );
#endif
int sr = sendto(sd, (char*)&pckt, sizeof( pckt.hdr ) + rsize , 0, addr, addr_len);
if( sr <= 0 )
{
ping_failed_to_send = 1;
if( using_regular_ping )
{
ERRMB("Ping send failed:\n%s (%d)\n", strerror(errno), errno);
}
}
else
{
ping_failed_to_send = 0;
}
}
}
void ping(struct sockaddr *addr, socklen_t addr_len )
{
int cnt=1;
using_regular_ping = 1;
#ifdef WIN32
{
//Setup windows socket for nonblocking io.
unsigned long iMode = 1;
ioctlsocket(sd, FIONBIO, &iMode);
}
#else
if ( fcntl(sd, F_SETFL, O_NONBLOCK) != 0 )
ERRM("Warning: Request nonblocking I/O failed.");
#endif
double stime = OGGetAbsoluteTime();
struct packet pckt;
do
{
int rsize = load_ping_packet( pckt.msg, sizeof( pckt.msg ) );
memset( &pckt.hdr, 0, sizeof( pckt.hdr ) ); //This needs to be here, but I don't know why, since I think the struct is fully populated.
#ifdef __FreeBSD__
pckt.hdr.icmp_code = 0;
pckt.hdr.icmp_type = ICMP_ECHO;
pckt.hdr.icmp_id = pid;
pckt.hdr.icmp_seq = cnt++;
pckt.hdr.icmp_cksum = checksum((const unsigned char *)&pckt, sizeof( pckt.hdr ) + rsize );
#else
pckt.hdr.code = 0;
pckt.hdr.type = (psaddr.sin6_family == AF_INET) ? ICMP_ECHO : ICMP6_ECHO_REQUEST;
pckt.hdr.un.echo.id = pid;
pckt.hdr.un.echo.sequence = cnt++;
pckt.hdr.checksum = checksum((const unsigned char *)&pckt, sizeof( pckt.hdr ) + rsize );
#endif
int sr = sendto(sd, (char*)&pckt, sizeof( pckt.hdr ) + rsize , 0, addr, addr_len);
if( sr <= 0 )
{
ping_failed_to_send = 1;
if( using_regular_ping )
{
ERRMB("Ping send failed:\n%s (%d)\n", strerror(errno), errno);
}
}
else
{
ping_failed_to_send = 0;
}
if( precise_ping )
{
double ctime;
do
{
ctime = OGGetAbsoluteTime();
if( pingperiodseconds >= 1000 ) stime = ctime;
} while( ctime < stime + pingperiodseconds );
stime += pingperiodseconds;
}
else
{
if( pingperiodseconds > 0 )
{
uint32_t dlw = 1000000.0*pingperiodseconds;
OGUSleep( dlw );
}
}
} while( pingperiodseconds >= 0 );
//close( sd ); //Hacky, we don't close here because SD doesn't come from here, rather from ping_setup. We may want to run this multiple times.
}
void ping_setup(const char * strhost, const char * device)
{
pid = getpid();
#ifdef WIN32
WSADATA wsaData;
int r = WSAStartup(MAKEWORD(2,2), &wsaData);
if( r )
{
ERRM( "Fault in WSAStartup\n" );
exit( -2 );
}
#endif
#ifdef WIN32
sd = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, 0, 0, WSA_FLAG_OVERLAPPED);
{
int lttl = 0xff;
if (setsockopt(sd, IPPROTO_IP, IP_TTL, (const char*)<tl, sizeof(lttl)) == SOCKET_ERROR)
{
printf( "Warning: No IP_TTL.\n" );
}
}
#else
// resolve host
psaddr_len = sizeof(psaddr);
if( strhost )
resolveName((struct sockaddr*) &psaddr, &psaddr_len, strhost, AF_UNSPEC);
else
psaddr.sin6_family = AF_INET;
sd = createSocket();
setTTL(sd);
if(device)
{
if( setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device) +1) != 0)
{
ERRM("Error: Failed to set Device option. Are you root? Or can do sock_raw sockets?\n");
exit( -1 );
}
}
#endif
if ( sd < 0 )
{
ERRM("Error: Could not create raw socket\n");
exit(0);
}
}
#endif // WIN_USE_NO_ADMIN_PING
void do_pinger( )
{
ping((struct sockaddr*) &psaddr, psaddr_len );
}
// used by the ERRMB makro from error_handling.h
char errbuffer[1024];
================================================
FILE: ping.h
================================================
#ifndef _PING_H
#define _PING_H
#include <stdint.h>
#ifdef WIN32
typedef int socklen_t;
struct sockaddr;
#else
#include <sys/socket.h>
#endif
unsigned short checksum(const unsigned char *b, uint16_t len);
//Callback (when received)
void display(uint8_t *buf, int bytes);
//Callback (before sending)
//return value = # of bytes to send in ping message.
int load_ping_packet( uint8_t * buffer, int buffersize );
void listener();
void ping(struct sockaddr *addr, socklen_t addr_len );
void do_pinger( );
void singleping(struct sockaddr *addr, socklen_t addr_len ); // If using this, must call ping_setup( 0, 0); to begin.
//If pingperiodseconds = -1, run ping/do_pinger once and exit.
extern float pingperiodseconds;
extern int precise_ping; //if 0, use minimal CPU, but ping send-outs are only approximate, if 1, spinlock until precise time for ping is hit.
extern int ping_failed_to_send;
void ping_setup(const char * strhost, const char * device);
#endif
================================================
FILE: resolve.c
================================================
#include "resolve.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "error_handling.h"
#ifdef WIN32
#include <ws2tcpip.h>
#else // !WIN32
#include <arpa/inet.h> // inet_pton (parsing ipv4 and ipv6 notation)
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#endif
int resolveName(struct sockaddr* addr, socklen_t* addr_len, const char* hostname, int family)
{
int result;
struct addrinfo* res = NULL;
// zero addr
memset(addr, 0, *addr_len);
// try to parse ipv4
if(family == AF_UNSPEC || family == AF_INET) {
result = inet_pton(AF_INET, hostname, &((struct sockaddr_in*) addr)->sin_addr);
if(result == 1)
{
addr->sa_family = AF_INET;
*addr_len = sizeof(struct sockaddr_in);
return 1;
}
}
// try to parse ipv6
if(family == AF_UNSPEC || family == AF_INET6) {
result = inet_pton(AF_INET6, hostname, &((struct sockaddr_in6*) addr)->sin6_addr);
if(result == 1)
{
addr->sa_family = AF_INET6;
*addr_len = sizeof(struct sockaddr_in6);
return 1;
}
}
// try to resolve DNS
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = family; // AF_UNSPEC, AF_INET, AF_INET6
hints.ai_socktype = 0; // 0 = any, SOCK_STREAM, SOCK_DGRAM
hints.ai_protocol = 0; // 0 = any
hints.ai_flags = 0; // no flags
result = getaddrinfo(hostname, NULL, &hints, &res);
if( result != 0)
{
ERRM( "Error: cannot resolve hostname %s: %s\n", hostname, gai_strerror(result) );
exit( -1 );
}
if(res->ai_addrlen > *addr_len)
{
// error - this should not happen
ERRM( "Error addr is to short. required length: %d, available length: %d", res->ai_addrlen, *addr_len );
freeaddrinfo(res);
exit( -1 );
}
memcpy(addr, res->ai_addr, res->ai_addrlen);
*addr_len = res->ai_addrlen;
freeaddrinfo(res);
return 1;
}
================================================
FILE: resolve.h
================================================
#ifndef _RESOLVE_H
#define _RESOLVE_H
#ifdef WIN32
typedef int socklen_t;
struct sockaddr;
#else
#include <sys/socket.h>
#endif
// try to parse hostname
// * as dot notation (1.1.1.1)
// * as ipv6 notation (abcd:ef00::1)
// * as hostname (resolve DNS)
// returns 1 on success
// family can be used to force ipv4 or ipv6. Use AF_UNSPEC (allow both), AF_INET or AF_INET6 to force ipv4 or ipv6
int resolveName(struct sockaddr* addr, socklen_t* addr_len, const char* hostname, int family);
#endif
================================================
FILE: resources.rc
================================================
#ifndef WIN_USE_NO_ADMIN_PING
1 24 "uac.manifest"
#endif
#include <windows.h>
LANGUAGE 9, SUBLANG_DEFAULT
IPDialog DIALOG 10, 10, 340, 100
STYLE WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "cnping"
BEGIN
LTEXT "Usage: cnping [host] [period] [extra size] [y-axis scaling]", 0, 90, 0, 300, 20, SS_LEFT
LTEXT "[host] -- domain or IP address of icmp ping, or http://host", 0, 90, 15, 300, 20, SS_LEFT
LTEXT "[period] -- period in seconds (optional), default is 0.02", 0, 90, 30, 300, 20, SS_LEFT
LTEXT "[extra size] -- ping packet extra size (above 12), optional, default = 0", 0, 90, 45, 300, 10, SS_LEFT
LTEXT "[const y-axis scaling] -- use a fixed scaling factor instead of auto scaling", 0, 90, 60, 300, 10, SS_LEFT
EDITTEXT 3, 1, 15, 88, 12
EDITTEXT 4, 1, 30, 88, 12
EDITTEXT 5, 1, 45, 88, 12
EDITTEXT 6, 1, 60, 88, 12
PUSHBUTTON "&Ping", 7, 25, 80, 100, 10
PUSHBUTTON "&Close", 8, 175, 80, 100, 10
END
MAIN_ICON ICON "cnping.ico"
================================================
FILE: searchnet.c
================================================
#include <stdio.h>
#include "ping.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdint.h>
#include <stdlib.h>
#include "resolve.h"
#include "rawdraw/os_generic.h"
uint32_t my_random_key;
uint8_t send_id[4];
extern float pingperiodseconds;
void * PingListen( void * r )
{
listener();
printf( "Fault on listen.\n" );
exit( -2 );
}
void display(uint8_t *buf, int bytes)
{
uint32_t reqid = ((uint32_t)buf[0+1] << 24) | (buf[1+1]<<16) | (buf[2+1]<<8) | (buf[3+1]);
if( reqid != my_random_key ) return;
printf( "%d.%d.%d.%d\n", buf[4+1], buf[5+1], buf[6+1], buf[7+1] );
}
int load_ping_packet( uint8_t * buffer, int bufflen )
{
buffer[0+1] = my_random_key >> 24;
buffer[1+1] = my_random_key >> 16;
buffer[2+1] = my_random_key >> 8;
buffer[3+1] = my_random_key >> 0;
buffer[4+1] = send_id[0];
buffer[5+1] = send_id[1];
buffer[6+1] = send_id[2];
buffer[7+1] = send_id[3];
return 12;
}
int main( int argc, char ** argv )
{
uint32_t offset;
int mask;
in_addr_t base;
char dispip[32];
float speed;
ping_setup( 0, 0);
OGCreateThread( PingListen, 0 );
srand( ((int)(OGGetAbsoluteTime()*10000)) );
my_random_key = rand();
if( argc != 4 )
{
fprintf( stderr, "Usage: [searchnet IP net] [mask (single #, i.e. 24)] [speed (in seconds per attempt)]\n" );
return -1;
}
base = ntohl(inet_addr( argv[1] ));
mask = 1<<(32-atoi(argv[2]));
speed = atof(argv[3]);
base &= ~(mask-1);
printf( "Base: %08x / Mask: %08x\n", base, mask );
for( offset = 0; offset < mask; offset++ )
{
uint32_t cur = base + offset;
sprintf( dispip, "%d.%d.%d.%d", (cur>>24)&0xff, (cur>>16)&0xff, (cur>>8)&0xff, (cur)&0xff );
send_id[0] = (cur>>24)&0xff;
send_id[1] = (cur>>16)&0xff;
send_id[2] = (cur>>8)&0xff;
send_id[3] = (cur)&0xff;
// printf( "Pinging: %s\n", dispip );
pingperiodseconds = -1;
struct sockaddr_in6 psaddr;
socklen_t psaddr_len = sizeof(psaddr);
resolveName((struct sockaddr*) &psaddr, &psaddr_len, dispip, AF_UNSPEC);
singleping((struct sockaddr*) &psaddr, psaddr_len );
OGUSleep( (int)(speed * 1000000) );
}
return 0;
}
================================================
FILE: uac.manifest
================================================
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!-- This is no longer needed if you compile with WIN_USE_NO_ADMIN_PING. It is only required for raw packet access -->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0" processorArchitecture="X86" name="cnping" type="win32"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
gitextract_6s2_y85m/ ├── .github/ │ ├── dependabot.yml │ └── workflows/ │ └── build.yml ├── .gitignore ├── .gitmodules ├── .travis.yml ├── LICENSE ├── LICENSE.BSD-3-Clause ├── LICENSE.MIT ├── Makefile ├── README.md ├── cnping.1 ├── cnping.c ├── debian/ │ ├── changelog │ ├── control │ ├── copyright │ ├── install │ ├── postinst │ ├── rules │ └── source/ │ └── format ├── error_handling.h ├── freedesktop/ │ ├── com.github.cntools.cnping.desktop │ └── com.github.cntools.cnping.metainfo.xml ├── httping.c ├── httping.h ├── ping.c ├── ping.h ├── resolve.c ├── resolve.h ├── resources.rc ├── searchnet.c └── uac.manifest
SYMBOL INDEX (53 symbols across 7 files)
FILE: cnping.c
function HandleNewPacket (line 89) | void HandleNewPacket( int seqno )
function HandleGotPacket (line 107) | void HandleGotPacket( int seqno, int timeout )
function HTTPingCallbackStart (line 143) | void HTTPingCallbackStart( int seqno )
function HTTPingCallbackGot (line 149) | void HTTPingCallbackGot( int seqno )
function display (line 154) | void display(uint8_t *buf, int bytes)
function load_ping_packet (line 165) | int load_ping_packet( uint8_t * buffer, int bufflen )
function HandleKey (line 203) | void HandleKey( int keycode, int bDown )
function HandleButton (line 244) | void HandleButton( int x, int y, int button, int bDown ){}
function HandleMotion (line 245) | void HandleMotion( int x, int y, int mask ){}
function HandleDestroy (line 246) | void HandleDestroy() { exit(0); }
function GetWindMaxPingTime (line 249) | double GetWindMaxPingTime( void )
function DrawMainText (line 273) | void DrawMainText( const char * stbuf )
function DrawFrameHistogram (line 287) | void DrawFrameHistogram()
function DrawFrame (line 421) | void DrawFrame( void )
function RegString (line 541) | int RegString( int write, char * data, DWORD len )
function INT_PTR (line 570) | INT_PTR CALLBACK TextEntry( HWND hwndDlg, UINT uMsg, WPARAM wParam, ...
function main (line 644) | int main( int argc, const char ** argv )
FILE: httping.c
function DoHTTPing (line 43) | void DoHTTPing( const char * addy, double minperiod, int * seqnoptr, vol...
type HTTPPingLaunch (line 195) | struct HTTPPingLaunch
type HTTPPingLaunch (line 210) | struct HTTPPingLaunch
type HTTPPingLaunch (line 210) | struct HTTPPingLaunch
type HTTPPingLaunch (line 221) | struct HTTPPingLaunch
type HTTPPingLaunch (line 221) | struct HTTPPingLaunch
function StartHTTPing (line 253) | int StartHTTPing( const char * addy, double minperiod, const char * device)
FILE: ping.c
type sockaddr_in6 (line 23) | struct sockaddr_in6
function ping_setup (line 54) | void ping_setup(const char * strhost, const char * device)
function listener (line 68) | void listener()
type repl_t (line 96) | struct repl_t
type sockaddr_in (line 103) | struct sockaddr_in
function singleping (line 130) | void singleping(struct sockaddr *addr, socklen_t addr_len )
function ping (line 167) | void ping(struct sockaddr *addr, socklen_t addr_len )
type icmphdr (line 247) | struct icmphdr
type packet (line 272) | struct packet
function checksum (line 286) | uint16_t checksum( const unsigned char * start, uint16_t len )
function setTTL (line 302) | void setTTL(int sock)
function isICMPResponse (line 316) | int isICMPResponse(unsigned char* buf, int bytes)
function createSocket (line 336) | int createSocket()
function listener (line 351) | void listener()
function singleping (line 412) | void singleping(struct sockaddr *addr, socklen_t addr_len )
function ping (line 465) | void ping(struct sockaddr *addr, socklen_t addr_len )
function ping_setup (line 539) | void ping_setup(const char * strhost, const char * device)
function do_pinger (line 597) | void do_pinger( )
FILE: ping.h
type socklen_t (line 6) | typedef int socklen_t;
type sockaddr (line 7) | struct sockaddr
type sockaddr (line 22) | struct sockaddr
type sockaddr (line 25) | struct sockaddr
FILE: resolve.c
function resolveName (line 18) | int resolveName(struct sockaddr* addr, socklen_t* addr_len, const char* ...
FILE: resolve.h
type socklen_t (line 5) | typedef int socklen_t;
type sockaddr (line 6) | struct sockaddr
type sockaddr (line 17) | struct sockaddr
FILE: searchnet.c
function display (line 23) | void display(uint8_t *buf, int bytes)
function load_ping_packet (line 32) | int load_ping_packet( uint8_t * buffer, int bufflen )
function main (line 48) | int main( int argc, char ** argv )
Condensed preview — 31 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (72K chars).
[
{
"path": ".github/dependabot.yml",
"chars": 120,
"preview": "version: 2\nupdates:\n - package-ecosystem: \"github-actions\"\n directory: \"/\"\n schedule:\n interval: \"monthly\"\n\n"
},
{
"path": ".github/workflows/build.yml",
"chars": 1663,
"preview": "name: Build cnping\n\non:\n push:\n pull_request:\n workflow_dispatch:\n\njobs:\n Build-for-Linux:\n runs-on: ubuntu-lates"
},
{
"path": ".gitignore",
"chars": 145,
"preview": "*.o\n*.exe\ncnping\ncnping-mousey\nsearchnet\ntags\n\ndebian/.debhelper\ndebian/cnping\ndebian/cnping.substvars\ndebian/debhelper-"
},
{
"path": ".gitmodules",
"chars": 80,
"preview": "[submodule \"rawdraw\"]\n\tpath = rawdraw\n\turl = https://github.com/cntools/rawdraw\n"
},
{
"path": ".travis.yml",
"chars": 1146,
"preview": "language: c\ndist: bionic\n\nbefore_install:\n- sudo apt-get -qq update\n- sudo apt-get install -y make libxinerama-dev libxe"
},
{
"path": "LICENSE",
"chars": 97,
"preview": "//Rawdraw and CNPing may be licensed feely under the MIT-x11\n//or NewBSD Licenses. You choose!\n\n"
},
{
"path": "LICENSE.BSD-3-Clause",
"chars": 1429,
"preview": "BSD 3-Clause License\n\nCopyright (c) 2018 Charles Lohr\n\nRedistribution and use in source and binary forms, with or withou"
},
{
"path": "LICENSE.MIT",
"chars": 1069,
"preview": "MIT License\n\nCopyright (c) 2018 Charles Lohr\n\nPermission is hereby granted, free of charge, to any person obtaining a co"
},
{
"path": "Makefile",
"chars": 3108,
"preview": "CFLAGS ?= -s -Os -I/opt/X11/include -Wall\nLDFLAGS ?= -s -L/opt/X11/lib/\nCC? = gcc\n\nICONSPATH = freedesktop/icons/hicolor"
},
{
"path": "README.md",
"chars": 2247,
"preview": "cnping\n======\n\nMinimal Graphical IPV4 Ping/HTTP Ping Tool. (also comes with searchnet, like nmap but smaller and simple"
},
{
"path": "cnping.1",
"chars": 1363,
"preview": ".TH CNPING 1 \"Dec 2020\" \"version 1.0\" \"User Manuals\"\n.SH \"NAME\"\ncnping \\- Minimal Graphical IPv4 Ping/HTTP Ping Tool\n.SH"
},
{
"path": "cnping.c",
"chars": 19047,
"preview": "//Copyright (c) 2011-2019 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose.\n\n#include <stdio.h>\n#includ"
},
{
"path": "debian/changelog",
"chars": 132,
"preview": "cnping (1.0.0) unstable; urgency=medium\n\n * Initial Release.\n\n -- Charles Lohr <lohr85@gmail.com> Sun, 04 Dec 2022 14:"
},
{
"path": "debian/control",
"chars": 1078,
"preview": "Source: cnping\nSection: net\nPriority: optional\nMaintainer: Charles Lohr <lohr85@gmail.com>\nBuild-Depends: debhelper-comp"
},
{
"path": "debian/copyright",
"chars": 3608,
"preview": "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: cnping\nUpstream-Contact: Charl"
},
{
"path": "debian/install",
"chars": 203,
"preview": "cnping /usr/bin/\nfreedesktop/com.github.cntools.cnping.desktop /usr/share/applications/\nfreedesktop/com.github.cntools.c"
},
{
"path": "debian/postinst",
"chars": 623,
"preview": "#!/bin/sh\n\nset -e\n\nPROGRAM=$(dpkg-divert --truename /usr/bin/cnping)\n\nif [ \"$1\" = configure ]; then\n # If we have set"
},
{
"path": "debian/rules",
"chars": 30,
"preview": "#!/usr/bin/make -f\n\n%:\n\tdh $@\n"
},
{
"path": "debian/source/format",
"chars": 13,
"preview": "3.0 (native)\n"
},
{
"path": "error_handling.h",
"chars": 558,
"preview": "#ifndef _ERROR_HANDLING\n#define _ERROR_HANDLING\n\nextern char errbuffer[1024];\n\n#ifdef WIN32\n\n#ifndef _MSC_VER\n#define ER"
},
{
"path": "freedesktop/com.github.cntools.cnping.desktop",
"chars": 315,
"preview": "[Desktop Entry]\nVersion=1.0\nName=cnping\nComment=Minimal Graphical IP Ping/HTTP Ping Tool\nType=Application\nIcon=com.githu"
},
{
"path": "freedesktop/com.github.cntools.cnping.metainfo.xml",
"chars": 1408,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<component type=\"desktop-application\">\n <id>com.github.cntools.cnping</id>\n <na"
},
{
"path": "httping.c",
"chars": 6022,
"preview": "#include \"httping.h\"\n#include <errno.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#"
},
{
"path": "httping.h",
"chars": 350,
"preview": "#ifndef _HTTPING_H\n#define _HTTPING_H\n\n//Callbacks (when started/received)\nvoid HTTPingCallbackStart( int seqno );\nvoid "
},
{
"path": "ping.c",
"chars": 12469,
"preview": "//Copyright 2017 <>< C. Lohr, under the MIT/x11 License\n//Rewritten from Sean Walton and Macmillan Publishers.\n//Most of"
},
{
"path": "ping.h",
"chars": 969,
"preview": "#ifndef _PING_H\n#define _PING_H\n\n#include <stdint.h>\n#ifdef WIN32\n\ttypedef int socklen_t;\n\tstruct sockaddr;\n#else\n\t#incl"
},
{
"path": "resolve.c",
"chars": 1820,
"preview": "#include \"resolve.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"error_handling.h\"\n\n#ifdef WIN"
},
{
"path": "resolve.h",
"chars": 502,
"preview": "#ifndef _RESOLVE_H\n#define _RESOLVE_H\n\n#ifdef WIN32\n\ttypedef int socklen_t;\n\tstruct sockaddr;\n#else\n\t#include <sys/socke"
},
{
"path": "resources.rc",
"chars": 1158,
"preview": "#ifndef WIN_USE_NO_ADMIN_PING\n1 24 \"uac.manifest\"\n#endif\n\n#include <windows.h>\n\nLANGUAGE 9, SUBLANG_DEFAULT\nIPDialog DIA"
},
{
"path": "searchnet.c",
"chars": 2114,
"preview": "#include <stdio.h>\n#include \"ping.h\"\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <st"
},
{
"path": "uac.manifest",
"chars": 596,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\n<!-- This is no longer needed if you compile with WIN_USE_NO_ADM"
}
]
About this extraction
This page contains the full source code of the cnlohr/cnping GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 31 files (63.9 KB), approximately 21.4k tokens, and a symbol index with 53 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.