Tuesday, October 27, 2026

this is how to ping localhost

// Simple ICMP ping for macOS (also works on Linux where unprivileged ICMP is allowed).

// Build: clang++ -std=c++17 -Wall -o myping ping.cpp

// Run:   ./myping localhost [count]


#include <arpa/inet.h>

#include <netdb.h>

#include <netinet/in.h>

#include <sys/socket.h>

#include <sys/time.h>

#include <unistd.h>


#include <chrono>

#include <cstdint>

#include <cstdio>

#include <cstdlib>

#include <cstring>

#include <iostream>

#include <thread>


struct IcmpHeader {

    uint8_t  type;

    uint8_t  code;

    uint16_t checksum;

    uint16_t id;

    uint16_t seq;

};


static const uint8_t ICMP_ECHO_REQUEST = 8;

static const uint8_t ICMP_ECHO_REPLY   = 0;


uint16_t checksum(const void* data, size_t len) {

    const uint16_t* p = static_cast<const uint16_t*>(data);

    uint32_t sum = 0;

    while (len > 1) { sum += *p++; len -= 2; }

    if (len == 1) sum += *reinterpret_cast<const uint8_t*>(p);

    sum = (sum >> 16) + (sum & 0xFFFF);

    sum += (sum >> 16);

    return static_cast<uint16_t>(~sum);

}


int main(int argc, char* argv[]) {

    if (argc < 2) {

        std::cerr << "Usage: " << argv[0] << " <host> [count]\n";

        return 1;

    }

    const char* host = argv[1];

    int count = (argc >= 3) ? std::atoi(argv[2]) : 4;


    // Resolve hostname to IPv4 address

    addrinfo hints{}, *res = nullptr;

    hints.ai_family = AF_INET;

    hints.ai_socktype = SOCK_DGRAM;

    if (int rc = getaddrinfo(host, nullptr, &hints, &res); rc != 0) {

        std::cerr << "Cannot resolve " << host << ": " << gai_strerror(rc) << "\n";

        return 1;

    }

    sockaddr_in dest = *reinterpret_cast<sockaddr_in*>(res->ai_addr);

    freeaddrinfo(res);


    char ipstr[INET_ADDRSTRLEN];

    inet_ntop(AF_INET, &dest.sin_addr, ipstr, sizeof(ipstr));


    // SOCK_DGRAM + IPPROTO_ICMP lets a normal user send pings on macOS (no sudo needed)

    int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);

    if (sock < 0) {

        perror("socket");

        return 1;

    }


    timeval tv{2, 0};  // 2 second receive timeout

    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));


    std::printf("PING %s (%s)\n", host, ipstr);


    const size_t payloadSize = 56;

    int sent = 0, received = 0;

    double minRtt = 1e9, maxRtt = 0, totalRtt = 0;

    uint16_t id = static_cast<uint16_t>(getpid() & 0xFFFF);


    for (int seq = 0; seq < count; ++seq) {

        char packet[sizeof(IcmpHeader) + payloadSize];

        std::memset(packet, 0, sizeof(packet));


        auto* hdr = reinterpret_cast<IcmpHeader*>(packet);

        hdr->type = ICMP_ECHO_REQUEST;

        hdr->code = 0;

        hdr->id = htons(id);

        hdr->seq = htons(static_cast<uint16_t>(seq));

        std::memset(packet + sizeof(IcmpHeader), 0xA5, payloadSize);

        hdr->checksum = 0;

        hdr->checksum = checksum(packet, sizeof(packet));


        auto start = std::chrono::steady_clock::now();

        ssize_t n = sendto(sock, packet, sizeof(packet), 0,

                           reinterpret_cast<sockaddr*>(&dest), sizeof(dest));

        if (n < 0) {

            perror("sendto");

        } else {

            ++sent;

            bool gotReply = false;

            while (!gotReply) {

                char buf[1024];

                sockaddr_in from{};

                socklen_t fromLen = sizeof(from);

                ssize_t r = recvfrom(sock, buf, sizeof(buf), 0,

                                     reinterpret_cast<sockaddr*>(&from), &fromLen);

                if (r < 0) {

                    std::printf("Request timeout for icmp_seq %d\n", seq);

                    break;

                }

                auto end = std::chrono::steady_clock::now();


                // macOS includes the IP header in received data; Linux does not.

                size_t offset = 0;

                if ((static_cast<uint8_t>(buf[0]) >> 4) == 4)

                    offset = (static_cast<uint8_t>(buf[0]) & 0x0F) * 4;

                if (static_cast<size_t>(r) < offset + sizeof(IcmpHeader)) continue;


                auto* reply = reinterpret_cast<IcmpHeader*>(buf + offset);

                if (reply->type == ICMP_ECHO_REPLY && ntohs(reply->seq) == seq) {

                    double ms = std::chrono::duration<double, std::milli>(end - start).count();

                    char fromStr[INET_ADDRSTRLEN];

                    inet_ntop(AF_INET, &from.sin_addr, fromStr, sizeof(fromStr));

                    std::printf("%zd bytes from %s: icmp_seq=%d time=%.3f ms\n",

                                r - static_cast<ssize_t>(offset), fromStr, seq, ms);

                    ++received;

                    totalRtt += ms;

                    if (ms < minRtt) minRtt = ms;

                    if (ms > maxRtt) maxRtt = ms;

                    gotReply = true;

                }

            }

        }


        if (seq < count - 1)

            std::this_thread::sleep_for(std::chrono::seconds(1));

    }


    close(sock);


    std::printf("\n--- %s ping statistics ---\n", host);

    std::printf("%d packets transmitted, %d received, %.1f%% packet loss\n",

                sent, received, sent ? 100.0 * (sent - received) / sent : 0.0);

    if (received > 0)

        std::printf("round-trip min/avg/max = %.3f/%.3f/%.3f ms\n",

                    minRtt, totalRtt / received, maxRtt);


    return received > 0 ? 0 : 1;

}

No comments:

Post a Comment

no thanks

Note: Only a member of this blog may post a comment.

this no one will understand i promise

  #!/usr/bin/env bash # pyreg.sh: registry-style store in pure bash. # # Here the directory tree IS the registry: keys are directories under...