/*
 * $Id: icmp.c,v 1.4.66.4 2003/06/13 02:18:30 squidadm Exp $
 *
 * DEBUG: section 37    ICMP Routines
 * AUTHOR: Duane Wessels
 */
//errno fixed
#include "squidDeclaration.h"


#include "squid.h"
 #define INCL_DOS
 #define INCL_DOSERRORS
 #include <os2.h>
#include "OS2PipesCS.hpp"


#if USE_ICMP

#define S_ICMP_ECHO    1
#if ALLOW_SOURCE_PING
#define S_ICMP_ICP     2
#endif
#define S_ICMP_DOM     3

static PF icmpRecv;
static void icmpSend(pingerEchoData * pkt, int len);
#if ALLOW_SOURCE_PING
static void icmpHandleSourcePing(const struct sockaddr_in *from, const char *buf);
#endif

int recvICMP(char *buf, int buflen);
int sendICMP(char *buf, int buflen);
int queryICMP(void);


static void
icmpSendEcho(struct in_addr to, int opcode, const char *payload, int len)
{
    static pingerEchoData pecho;
    if (payload && len == 0)
       len = strlen(payload);
    assert(len <= PINGER_PAYLOAD_SZ);
 #if defined(__IBMC__) || defined(__IBMCPP__)
    pecho.cmd = 0;
 #endif
    pecho.to = to;
    pecho.opcode = (unsigned char) opcode;
    pecho.psize = len;
    xmemcpy(pecho.payload, payload, len);
    icmpSend(&pecho, sizeof(pingerEchoData) - PINGER_PAYLOAD_SZ + len);
}

static void
icmpRecv(int unused1, void *unused2)
{
    int n,icmp_sock,ierrno;
    static int fail_count = 0;
    pingerReplyData preply;
    static struct sockaddr_in F;

    commSetSelect(icmp_sock_index, COMM_SELECT_READ, icmpRecv, NULL, 0);
    icmp_sock = squid_fd_table.fd_table[icmp_sock_index].GetSocket();
    memset(&preply, '\0', sizeof(pingerReplyData));
    statCounter.syscalls.sock.recvfroms++;
//e    n = recv(icmp_sock,
//e       (char *) &preply,
//e       sizeof(pingerReplyData),
//e       0);
    n = recvICMP((char *) &preply,sizeof(pingerReplyData));

    if (n < 0) {
       debug(37, 0) ("WARNING:icmpRecv:recvICMP: %s\n", xstdio_strerror());
       if (++fail_count == 10)
//e       if (++fail_count == 10 || ierrno == SOCECONNREFUSED)
           icmpClose();
       return;
    }
    fail_count = 0;
    if (n == 0)                        /* test probe from pinger */
       return;
    F.sin_family = AF_INET;
    F.sin_addr = preply.from;
    F.sin_port = 0;
    switch (preply.opcode) {
    case S_ICMP_ECHO:
       break;
#if ALLOW_SOURCE_PING
    case S_ICMP_ICP:
       icmpHandleSourcePing(&F, preply.payload);
       break;
#endif
    case S_ICMP_DOM:
       netdbHandlePingReply(&F, preply.hops, preply.rtt);
       break;
    default:
       debug(37, 1) ("icmpRecv: Bad opcode: %d\n", (int) preply.opcode);
       break;
    }
}

static void
icmpSend(pingerEchoData * pkt, int len)
{
    int x,icmp_sock, ierrno;
    if (icmp_sock_index < 0)
       return;
    icmp_sock = squid_fd_table.fd_table[icmp_sock_index].GetSocket();
    debug(37, 2) ("icmpSend: to %s, opcode %d, len %d\n",
       inet_ntoa(pkt->to), (int) pkt->opcode, pkt->psize);
//e    x = send(icmp_sock, (char *) pkt, len, 0);
    x = sendICMP((char *) pkt, len);

    if (x < 0) {
//e        ierrno = sock_errno();
//e       debug(37, 1) ("icmpSend: send: %s\n", xstrerror_i(ierrno));
       debug(37, 1) ("WARNING:icmpSend: send: %s\n", xstdio_strerror());

//e       if (ierrno == SOCECONNREFUSED || ierrno == SOCEPIPE) {
//e           icmpClose();
//e           return;
//e       }
    } else if (x != len) {
       debug(37, 2) ("icmpSend: Wrote %d of %d bytes\n", x, len);
    }
}

#if ALLOW_SOURCE_PING
static void
icmpHandleSourcePing(const struct sockaddr_in *from, const char *buf)
{
    const cache_key *key;
    icp_common_t header;
    const char *url;
    xmemcpy(&header, buf, sizeof(icp_common_t));
    url = buf + sizeof(icp_common_t);
    key = icpGetCacheKey(url, (int) header.reqnum);
    debug(37, 3) ("icmpHandleSourcePing: from %s, key '%s'\n",
       inet_ntoa(from->sin_addr), storeKeyText(key));
    /* call neighborsUdpAck even if ping_status != PING_WAITING */
    neighborsUdpAck(key, &header, from);
}

#endif

#endif /* USE_ICMP */


#if ALLOW_SOURCE_PING
void
icmpSourcePing(struct in_addr to, const icp_common_t * header, const char *url)
{
#if USE_ICMP
    char *payload;
    int len;
    int ulen;
    debug(37, 3) ("icmpSourcePing: '%s'\n", url);
    if ((ulen = strlen(url)) > MAX_URL)
       return;
    payload = memAllocate(MEM_8K_BUF);
    len = sizeof(icp_common_t);
    xmemcpy(payload, header, len);
    strcpy(payload + len, url);
    len += ulen + 1;
    icmpSendEcho(to, S_ICMP_ICP, payload, len);
    memFree(payload, MEM_8K_BUF);
#endif
}
#endif

void
icmpDomainPing(struct in_addr to, const char *domain)
{
#if USE_ICMP
    debug(37, 3) ("icmpDomainPing: '%s'\n", domain);
    icmpSendEcho(to, S_ICMP_DOM, domain, 0);
#endif
}
/**************************************/
#ifdef _SQUID_OS2VAC_

static char ICMPThreadQueueNameR[] = "\\queues\\squid_pinger_r";
static char ICMPThreadQueueNameW[] = "\\queues\\squid_pinger_w";
static HQUEUE pingerHqR;
static HQUEUE pingerHqW;
static PID pidOwnerW;

int startPingerThread(char *PingerQueueR,char *PingerQueueW);

void icmpOpen(void)
{
#if USE_ICMP
   int rc,i;
   char str[80];
   char hello_stringA[]="hi hi";

   if(!Config.Program.pinger)
   {   debug(37, 1) ("icmpOpen pinger not started\n");
       return;
   }

   rc = DosCreateQueue(&pingerHqR,QUE_FIFO|QUE_CONVERT_ADDRESS,ICMPThreadQueueNameR);
   if (rc)
   {   debug(37, 0) ("WARNING:icmpOpen DosCreateQueue error rc=%u\n",rc);
       return;
   }


   rc = startPingerThread(ICMPThreadQueueNameR,ICMPThreadQueueNameW);
   if(rc == 0)
         debug(37, 1) ("Pinger thread started Ok\n");
   else
         debug(37, 0) ("WARNING: Pinger thread NOT started \n");

    rc = DosOpenQueue (&pidOwnerW,         /* PID of queue owner              */
                       &pingerHqW,     /* Handle for created queue        */
                       ICMPThreadQueueNameW);      /* Name of the queue to open       */
    if (rc)
    {   debug(37, 0) ("WARNING:icmpOpen DosOpenQueue error rc=%u\n",rc);
       return;
    }
    rc = recvICMP(str, sizeof(str));
    if(rc > 0)
    {  if(strcmp(str,"hi there\n"))
       { debug(37, 0) ("WARNING:icmpOpen Hello string wrong\n");
         return;
       }
    } else if (rc < 0) {
       debug(37, 0) ("WARNING:icmpOpen recvICMP error\n");
       return;
    }
    rc = sendICMP((char *)hello_stringA , strlen(hello_stringA)+1);
    for(i=0;i<100;i++)
    {   DosSleep(10);
        if(icmp_sock_index >= 0)
                        break;
    }
    commSetSelect(icmp_sock_index, COMM_SELECT_READ, icmpRecv, NULL, 0);

#endif
}

int queryICMP(void)
{
    ULONG numMsg;
    int rc;
    rc = DosQueryQueue(pingerHqR, &numMsg);
    if(numMsg == 0)
        return 0;
    return numMsg;
}

int recvICMP(char *buf, int buflen)
{
   REQUESTDATA      Request;
   ULONG            ulDataLen,numMsg;
   BYTE             bPriority;
   char *buffer;
   int rc,rc0;

    rc = DosQueryQueue(pingerHqR, &numMsg);
    if(numMsg == 0)
        return 0;
    rc = DosReadQueue (pingerHqR,          /* Queue to read from          */
                       &Request,              /* Request data from write     */
                       &ulDataLen,            /* Length of data returned     */
                       (PPVOID) &buffer,   /* The data                    */
                       0L,                    /* Remove first element        */
                       DCWW_WAIT,             /* Wait for available data     */
                       &bPriority,             /* Priority of data (returned) */
                       0L);             /* Semaphore to use when not waiting */

    if(ulDataLen >  buflen)  ulDataLen = buflen;
    memcpy(buf,buffer,ulDataLen);
    if (rc)
    {   debug(37, 1) ("WARNING:recvICMP DosReadQueue error rc=%u\n",rc);
        rc0 = -1;
    } else {
        rc0 = ulDataLen;
debug(37, 8) ("recvICMP read %i bytes\n", rc0);
    }
    return rc0;
}

int sendICMP(char *buf, int buflen)
{   int rc,rc0 = buflen;
debug(37, 8) ("sendICMP sending %i bytes\n", buflen);

    rc = DosWriteQueue (pingerHqW,        /* Queue to write to           */
                        12345L,              /* Request data                */
                        buflen,  /* Length of data to write     */
                        buf,          /* Pointer to data             */
                        0L);              /* Priority (not applicable here) */

    if (rc)
    {   debug(37, 1) ("WARNING:sendICMP DosReadQueue error rc=%u\n",rc);
        rc0 = -1;
    }
    return rc0;
}


#else  //_SQUID_OS2VAC_

void
icmpOpen(void)
{
#if USE_ICMP
    char *args[3];
    int x,rc,i;
    int rfd;
    int wfd;
    args[0] = "(pinger)";
    args[1] = NULL;
    args[2] = NULL;


//    x = ipcCreate(IPC_UDP_SOCKET,
    x = ipcCreate(IPC_DGRAM,
       Config.Program.pinger,
       args,
       "Pinger Socket",
       &rfd,
       &wfd);
    if (x < 0)
       return;


    assert(rfd == wfd);
    icmp_sock_index = rfd;
    fd_note(icmp_sock_index, "pinger");
    commSetSelect(icmp_sock_index, COMM_SELECT_READ, icmpRecv, NULL, 0);
    commSetTimeout(icmp_sock_index, -1, NULL, NULL);
    debug(37, 1) ("Pinger socket opened on FD %d\n", icmp_sock_index);
#endif
}

#endif //_SQUID_OS2VAC_

void
icmpClose(void)
{
#if USE_ICMP
    int icmp_sock,rc;
//    if (icmp_sock_index < 0)
//       return;
    debug(37, 1) ("Closing Pinger socket on FD %d\n", icmp_sock_index);

#if defined(__IBMC__) || defined(__IBMCPP__)
{
    pingerEchoData pecho;
    pecho.cmd = 1;
//    pecho.to = 0;
    pecho.opcode = (unsigned char) 0;
    pecho.psize = 0;
    icmpSend(&pecho, sizeof(pingerEchoData) - PINGER_PAYLOAD_SZ + 0);
    DosSleep(100); //какая-то гарантия
    rc = DosCloseQueue(pingerHqR);         /* Close the queue */
    if (rc!= NO_ERROR) {
       debug(37, 1) ("WARNING:icmpClose:DosCloseQueue error: return code = %u\n", rc);
       return;
    }
    pingerHqR = NULL;

}
#endif

   if(icmp_sock_index >= 0)
   {
    icmp_sock = squid_fd_table.fd_table[icmp_sock_index].GetSocket();
    rc = shutdown(icmp_sock,2);

    comm_close(icmp_sock_index);
    icmp_sock_index = -1;
   }
#endif
}
