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) ``` 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 .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 #include #include #include #include #if defined( WINDOWS ) || defined( WIN32 ) #ifdef _MSC_VER #define strdup _strdup #endif #include #else #ifdef __FreeBSD__ #include #include #endif #include #include #include #include #include #include #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 Sun, 04 Dec 2022 14:05:43 +0100 ================================================ FILE: debian/control ================================================ Source: cnping Section: net Priority: optional Maintainer: Charles Lohr 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 Source: https://github.com/cntools/cnping/ Files: * Copyright: 2022 Charles Lohr License: BSD-3-Clause or MIT Files: debian/* Copyright: 2022 Charles Lohr 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 . . 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 ================================================ com.github.cntools.cnping cnping Minimal Graphical IPv4 Ping/HTTP Ping Tool com.github.cntools.cnping.desktop CC0-1.0

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.

cnping uses rawdraw so it is OS independent and very light weight.

https://github.com/cntools/cnping https://i.imgur.com/Yj5coKN.gif MIT or BSD-3-Clause https://github.com/cntools/cnping/issues
================================================ FILE: httping.c ================================================ #include "httping.h" #include #include #include #include #include #include "resolve.h" #ifndef TCC #include #include #else #include "tccheader.h" #endif #if defined( WIN32 ) || defined( WINDOWS ) #ifdef TCC #else #include #endif #define SOL_TCP IPPROTO_TCP #else #include #include #include #include #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 #include #include #include #include #include #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 #include #include #include #include #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 #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 #include #include #include #else // ! WIN32 #ifdef __FreeBSD__ #include #endif #include #include #include #include #if defined(__APPLE__) || defined(__FreeBSD__) #ifndef SOL_IP #define SOL_IP IPPROTO_IP #endif #endif #include #include #include #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>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 #ifdef WIN32 typedef int socklen_t; struct sockaddr; #else #include #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 #include #include #include "error_handling.h" #ifdef WIN32 #include #else // !WIN32 #include // inet_pton (parsing ipv4 and ipv6 notation) #include #include #include #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 #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 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 #include "ping.h" #include #include #include #include #include #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 ================================================