/*
 * ping program
 *
 * Copyright (C) 2010 Trey Hunner
 * Copyright (C) 2018 Isira Seneviratne
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "winsock2.h"
#include "ws2tcpip.h"
#include "iphlpapi.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <icmpapi.h>
#include <limits.h>

#include <windows.h>

#include "wine/debug.h"
#include "wine/heap.h"

WINE_DEFAULT_DEBUG_CHANNEL(ping);

static void usage(void)
{
    printf("Usage: ping [-n count] [-w timeout] [-l buffer_length] target_name\n\n"
           "Options:\n"
           "    -n  Number of echo requests to send.\n"
           "    -w  Timeout in milliseconds to wait for each reply.\n"
           "    -l  Length of send buffer.\n");
}

int __cdecl main(int argc, char** argv)
{
    unsigned int n = 4, i, w = 4000, l = 32;
    int res;
    int rec = 0, lost = 0, min = INT_MAX, max = 0;
    WSADATA wsa;
    HANDLE icmp_file;
    unsigned long ipaddr;
    DWORD retval, reply_size;
    char *send_data, ip[100], *hostname = NULL, rtt[16];
    void *reply_buffer;
    struct in_addr addr;
    ICMP_ECHO_REPLY *reply;
    float avg = 0;
    struct hostent *remote_host;

    if (argc == 1)
    {
        usage();
        exit(1);
    }

    for (i = 1; i < argc; i++)
    {
        if (argv[i][0] == '-' || argv[i][0] == '/')
        {
            switch (argv[i][1])
            {
            case 'n':
                if (i == argc - 1)
                {
                    printf( "Missing value for option %s\n", argv[i] );
                    exit(1);
                }
                n = atoi(argv[++i]);
                if (n == 0)
                {
                  printf("Bad value for option -n, valid range is from 1 to 4294967295.\n");
                  exit(1);
                }
                break;
            case 'w':
                if (i == argc - 1)
                {
                    printf( "Missing value for option %s\n", argv[i] );
                    exit(1);
                }
                w = atoi(argv[++i]);
                if (w == 0)
                {
                    printf("Bad value for option -w.\n");
                    exit(1);
                }
                break;
            case 'l':
                if (i == argc - 1)
                {
                    printf( "Missing value for option %s\n", argv[i] );
                    exit(1);
                }
                l = atoi(argv[++i]);
                if (l == 0)
                {
                    printf("Bad value for option -l.\n");
                    exit(1);
                }
                break;
            case '?':
                usage();
                exit(1);
            default:
                usage();
                WINE_FIXME( "this command currently only supports the -n, -w and -l parameters.\n" );
                exit(1);
            }
        }
        else
        {
            if (hostname)
            {
                printf( "Bad argument %s\n", argv[i] );
                exit(1);
            }
            hostname = argv[i];
        }
    }

    if (!hostname)
    {
        printf("Pass a host name.\n");
        return 1;
    }

    res = WSAStartup(MAKEWORD(2, 2), &wsa);
    if (res != 0)
    {
        printf("WSAStartup failed: %d\n", res);
        return 1;
    }

    remote_host = gethostbyname(hostname);
    if (remote_host == NULL)
    {
        printf("Ping request could not find host %s. Please check the name and try again.\n",
               hostname);
        return 1;
    }

    addr.s_addr = *(u_long *) remote_host->h_addr_list[0];
    strcpy(ip, inet_ntoa(addr));
    ipaddr = inet_addr(ip);
    if (ipaddr == INADDR_NONE)
    {
        printf("Could not get IP address of host %s.", hostname);
        return 1;
    }

    icmp_file = IcmpCreateFile();

    send_data = heap_alloc_zero(l);
    reply_size = sizeof(ICMP_ECHO_REPLY) + l + 8;
    /* The buffer has to hold 8 more bytes of data (the size of an ICMP error message). */
    reply_buffer = heap_alloc(reply_size);
    if (reply_buffer == NULL)
    {
        printf("Unable to allocate memory to reply buffer.\n");
        return 1;
    }

    printf("Pinging %s [%s] with %d bytes of data:\n", hostname, ip, l);
    for (i = 0; i < n; i++)
    {
        SetLastError(0);
        retval = IcmpSendEcho(icmp_file, ipaddr, send_data, l,
            NULL, reply_buffer, reply_size, w);
        if (retval != 0)
        {
            reply = (ICMP_ECHO_REPLY *) reply_buffer;
            if (reply->RoundTripTime >= 1)
                sprintf(rtt, "=%ld", reply->RoundTripTime);
            else
                strcpy(rtt, "<1");
            printf("Reply from %s: bytes=%d time%sms TTL=%d\n", ip, l,
                rtt, reply->Options.Ttl);
            if (reply->RoundTripTime > max)
                max = reply->RoundTripTime;
            if (reply->RoundTripTime < min)
                min = reply->RoundTripTime;
            avg += reply->RoundTripTime;
            rec++;
        }
        else
        {
            if (GetLastError() == IP_REQ_TIMED_OUT)
                puts("Request timed out.");
            else
                puts("PING: transmit failed. General failure.");
            lost++;
        }
        if (i < n - 1) Sleep(1000);
    }

    printf("\nPing statistics for %s\n", ip);
    printf("\tPackets: Sent = %d, Received = %d, Lost = %d (%.0f%% loss)\n",
        n, rec, lost, (float) lost / n * 100);
    if (rec != 0)
    {
        avg /= rec;
        printf("Approximate round trip times in milli-seconds:\n");
        printf("\tMinimum = %dms, Maximum = %dms, Average = %.0fms\n",
               min, max, avg);
    }

    heap_free(reply_buffer);
    return 0;
}