/* Copyright (C) 1998, 2001 Free Software Foundation, Inc. This file is part of GNU Inetutils. GNU Inetutils 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 2, or (at your option) any later version. GNU Inetutils is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 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 GNU Inetutils; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include /*#include -- deliberately not including this */ #include #include #include #include #include #include #include #include #include "ping.h" static void _ping_freebuf (PING *p); static int _ping_setbuf (PING *p); static size_t _ping_packetsize (PING *p); size_t _ping_packetsize (PING *p) { switch (p->ping_type) { case ICMP_TIMESTAMP: case ICMP_TIMESTAMPREPLY: return 20; default: return 8 + p->ping_datalen; } return 8; /* to keep compiler happy */ } PING * ping_init (int type, int ident) { int fd; struct protoent *proto; PING *p; /* Initialize raw ICMP socket */ if (!(proto = getprotobyname ("icmp"))) { fprintf (stderr, "ping: unknown protocol icmp.\n"); return NULL; } if ((fd = socket (AF_INET, SOCK_RAW, proto->p_proto)) < 0) { if (errno == EPERM) { fprintf (stderr, "ping: ping must run as root\n"); } return NULL; } /* Allocate PING structure and initialize it to default values */ if (!(p = malloc (sizeof (*p)))) { close (fd); return p; } memset (p, 0, sizeof (*p)); p->ping_fd = fd; p->ping_type = type; p->ping_count = 0; p->ping_interval = PING_INTERVAL; p->ping_datalen = sizeof (icmphdr_t); /* Make sure we use only 16 bits in this field, id for icmp is a u_short. */ p->ping_ident = ident & 0xFFFF; p->ping_cktab_size = PING_CKTABSIZE; return p; } void ping_set_type (PING *p, int type) { p->ping_type = type; } void ping_set_datalen (PING *p, size_t len) { _ping_freebuf (p); p->ping_datalen = len; } void _ping_freebuf (PING *p) { if (p->ping_buffer) { free (p->ping_buffer); p->ping_buffer = NULL; } } int _ping_setbuf (PING *p) { if (!p->ping_buffer) { p->ping_buffer = malloc (_PING_BUFLEN (p)); if (!p->ping_buffer) return -1; } if (!p->ping_cktab) { p->ping_cktab = malloc (p->ping_cktab_size); if (!p->ping_cktab) return -1; memset (p->ping_cktab, 0, p->ping_cktab_size); } return 0; } int ping_set_data (PING *p, void *data, size_t off, size_t len) { icmphdr_t *icmp; if (_ping_setbuf (p)) return -1; if (p->ping_datalen < off + len) return -1; icmp = (icmphdr_t *)p->ping_buffer; memcpy (icmp->icmp_data + off, data, len); return 0; } int ping_xmit (PING *p) { int i, buflen; if (_ping_setbuf (p)) return -1; buflen = _ping_packetsize (p); /* Mark sequence number as sent */ _PING_CLR (p, p->ping_num_xmit % p->ping_cktab_size); /* Encode ICMP header */ switch (p->ping_type) { case ICMP_ECHO: icmp_echo_encode (p->ping_buffer, buflen, p->ping_ident, p->ping_num_xmit); break; case ICMP_TIMESTAMP: icmp_timestamp_encode (p->ping_buffer, buflen, p->ping_ident, p->ping_num_xmit); break; case ICMP_ADDRESS: icmp_address_encode (p->ping_buffer, buflen, p->ping_ident, p->ping_num_xmit); break; default: icmp_generic_encode (p->ping_buffer, buflen, p->ping_type, p->ping_ident, p->ping_num_xmit); break; } i = sendto (p->ping_fd, (char *)p->ping_buffer, buflen, 0, (struct sockaddr*) &p->ping_dest, sizeof (struct sockaddr_in)); if (i < 0) { perror ("ping: sendto"); } else { p->ping_num_xmit++; if (i != buflen) printf ("ping: wrote %s %d chars, ret=%d\n", p->ping_hostname, buflen, i); } return 0; } static int my_echo_reply (PING *p, icmphdr_t *icmp) { struct ip *orig_ip = &icmp->icmp_ip; icmphdr_t *orig_icmp = (icmphdr_t *)(orig_ip + 1); return orig_ip->ip_dst.s_addr == p->ping_dest.sin_addr.s_addr && orig_ip->ip_p == IPPROTO_ICMP && orig_icmp->icmp_type == ICMP_ECHO && orig_icmp->icmp_id == p->ping_ident; } int ping_recv (PING *p) { int fromlen = sizeof (p->ping_from); int n, rc; icmphdr_t *icmp; struct ip *ip; int dupflag; if ((n = recvfrom (p->ping_fd, (char *)p->ping_buffer, _PING_BUFLEN (p), 0, (struct sockaddr *)&p->ping_from, &fromlen)) < 0) return -1; if ((rc = icmp_generic_decode (p->ping_buffer, n, &ip, &icmp)) < 0) { /*FIXME: conditional*/ fprintf (stderr,"packet too short (%d bytes) from %s\n", n, inet_ntoa (p->ping_from.sin_addr)); return -1; } switch (icmp->icmp_type) { case ICMP_ECHOREPLY: case ICMP_TIMESTAMPREPLY: case ICMP_ADDRESSREPLY: /* case ICMP_ROUTERADV:*/ if (icmp->icmp_id != p->ping_ident) return -1; if (rc) fprintf (stderr, "checksum mismatch from %s\n", inet_ntoa (p->ping_from.sin_addr)); p->ping_num_recv++; if (_PING_TST (p, icmp->icmp_seq % p->ping_cktab_size)) { p->ping_num_rept++; p->ping_num_recv--; dupflag = 1; } else { _PING_SET (p, icmp->icmp_seq % p->ping_cktab_size); dupflag = 0; } if (p->ping_event) (*p->ping_event)(dupflag ? PEV_DUPLICATE : PEV_RESPONSE, p->ping_closure, &p->ping_dest, &p->ping_from, ip, icmp, n); break; case ICMP_ECHO: case ICMP_TIMESTAMP: case ICMP_ADDRESS: return -1; default: if (!my_echo_reply(p, icmp)) return -1; if (p->ping_event) (*p->ping_event)(PEV_NOECHO, p->ping_closure, &p->ping_dest, &p->ping_from, ip, icmp, n); } return 0; } void ping_set_event_handler (PING *ping, ping_efp pf, void *closure) { ping->ping_event = pf; ping->ping_closure = closure; } void ping_set_count (PING *ping, int count) { ping->ping_count = count; } void ping_set_sockopt (PING *ping, int opt, void *val, int valsize) { setsockopt (ping->ping_fd, SOL_SOCKET, opt, (char*)&val, valsize); } void ping_set_interval (PING *ping, int interval) { ping->ping_interval = interval; } void ping_set_packetsize (PING *ping, int size) { ping->ping_datalen = size; } int ping_set_dest (PING *ping, char *host) { struct sockaddr_in *s_in = &ping->ping_dest; s_in->sin_family = AF_INET; if (inet_aton (host, &s_in->sin_addr)) { ping->ping_hostname = strdup (host); } else { struct hostent *hp = gethostbyname (host); if (!hp) return 1; s_in->sin_family = hp->h_addrtype; if (hp->h_length > (int)sizeof (s_in->sin_addr)) hp->h_length = sizeof (s_in->sin_addr); memcpy (&s_in->sin_addr, hp->h_addr, hp->h_length); ping->ping_hostname = strdup (hp->h_name); } return 0; }