/*
* Copyright 2001 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Niels Provos.
* 4. 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.
*/
#include "config.h"
#include <sys/param.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/queue.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <dnet.h>
#include <pcap.h>
#ifdef BSD
#include <pcap-int.h>
#endif
#include <event.h>
#include "buffer.h"
#include "g711.h"
#include "voip.h"
#define MAXTALKS 2
#define MAXSAMPLES 480
#define MAXBUFFER (10000*MAXSAMPLES)
extern int lnet_sock;
extern int pcap_off;
extern int debug;
int16_t lastidnr = 0;
/* XXX - Seen cache */
ip_addr_t tcp_src;
ip_addr_t tcp_dst;
u_int32_t tcp_seqnr;
/* XXX - bad */
char phone_number[1024];
u_char voip_magic_data[2] = {0x80, 0x00};
u_char voip_magic_start[2] = {0x80, 0x80};
extern struct event voipev;
/* buffer handler */
bh bhd;
/* Evil - we'll just voip_session = bufid */
struct voip_session {
struct in_addr src;
struct in_addr dst;
u_short sport;
u_short dport;
u_int32_t magic;
};
int
voip_seen(struct ip_hdr *ip, struct tcp_hdr *tcp)
{
if (ip->ip_src == tcp_src &&
ip->ip_dst == tcp_dst &&
tcp->th_seq == tcp_seqnr)
return (1);
tcp_src = ip->ip_src;
tcp_dst = ip->ip_dst;
tcp_seqnr = tcp->th_seq;
return (0);
}
void
voip_init(void)
{
bhd = buffer_new(MAXTALKS, MAXSAMPLES, MAXBUFFER);
buffer_init(bhd);
}
void
make_ident(ip_addr_t *src, u_short sport,
ip_addr_t *dst, u_short dport,
u_int32_t magic, bufid *pident)
{
u_char *ident = (u_char *)pident;
memset(ident, 0, sizeof(ident));
memcpy(ident, src, sizeof(*src)); ident += sizeof(*src);
memcpy(ident, dst, sizeof(*dst)); ident += sizeof(*dst);
memcpy(ident, &sport, sizeof(sport)); ident += sizeof(sport);
memcpy(ident, &dport, sizeof(dport)); ident += sizeof(dport);
memcpy(ident, &magic, sizeof(magic)); ident += sizeof(magic);
}
void
voip_sniff_tcp(u_char *user,
const struct pcap_pkthdr *pkthdr, const u_char *pkt)
{
struct ip_hdr *ip;
struct tcp_hdr *tcp;
struct voip_signal *voip;
struct addr src, dst;
u_char *p;
char *cmd, *what;
char extra[1024];
char tmp[2];
int len;
char srcname[MAXHOSTNAMELEN], dstname[MAXHOSTNAMELEN];
ip = (struct ip_hdr *)(pkt + pcap_off);
tcp = (struct tcp_hdr *)(pkt + pcap_off + (ip->ip_hl<<2));
voip = (struct voip_signal *)(pkt + pcap_off + (ip->ip_hl<<2) +
(tcp->th_off<<2));
p = (u_char *)(voip + 1);
len = ntohs(ip->ip_len) - (ip->ip_hl<<2) - (tcp->th_off<<2);
if (len < sizeof(struct voip_signal)) {
if (debug)
fprintf(stderr, "Bad packet length: %d\n", len);
return;
}
if (voip_seen(ip, tcp))
return;
addr_pack(&src, ADDR_TYPE_IP, IP_ADDR_BITS, &ip->ip_src, IP_ADDR_LEN);
addr_pack(&dst, ADDR_TYPE_IP, IP_ADDR_BITS, &ip->ip_dst, IP_ADDR_LEN);
strlcpy(srcname, addr_ntoa(&src), sizeof (srcname));
strlcpy(dstname, addr_ntoa(&dst), sizeof (dstname));
what = cmd = "";
extra[0] = '\0';
switch(voip->proto) {
case VOIP_IE_CON:
what = "Connection";
switch(voip->cmd) {
case VOIP_CON_PICKUP:
cmd = "Handset pick up";
phone_number[0] = '\0';
break;
case VOIP_CON_HANGUP:
cmd = "Handset hang up";
break;
case VOIP_CON_ALIVE:
if (!debug)
return;
cmd = "Keep Alive";
break;
default:
cmd = "Unknown command";
break;
}
break;
case VOIP_IE_USER:
what = "User Input";
switch (voip->cmd) {
case VOIP_USER_DIAL:
cmd = "Dial";
if (len == sizeof(struct voip_signal) + 4) {
snprintf(extra, sizeof(extra),
"(Digit %d)", p[0]);
}
tmp[0] = p[0] + '0';
tmp[1] = '\0';
strlcat(phone_number, tmp, sizeof (phone_number));
}
break;
case VOIP_IE_DISPLAY:
what = "Display";
switch (voip->cmd) {
case VOIP_DISPLAY_STATUS:
cmd = "Status";
if (len <= sizeof(struct voip_signal))
break;
len -= sizeof(struct voip_signal);
if (len < voip->plen)
break;
snprintf(extra, sizeof(extra), "\n\t(%s)", (char *)p);
break;
default:
cmd = "Unknown";
break;
}
break;
default:
what = "unknown";
cmd = "unknown";
break;
}
fprintf(stderr,
"%s -> %s: %s(0x%x) %s(0x%x) %s\n",
srcname, dstname,
what, voip->proto,
cmd, voip->cmd,
extra);
}
void
voip_sniff_udp(u_char *user,
const struct pcap_pkthdr *pkthdr, const u_char *pkt)
{
struct ip_hdr *ip;
struct udp_hdr *udp;
struct voip_hdr *voip;
bufid ident;
char srcname[MAXHOSTNAMELEN], dstname[MAXHOSTNAMELEN];
u_char *p;
u_int16_t buf[1024];
int i, len;
ip = (struct ip_hdr *)(pkt + pcap_off);
udp = (struct udp_hdr *)(pkt + pcap_off + (ip->ip_hl<<2));
voip = (struct voip_hdr *)(udp + 1);
p = (u_char *)(voip + 1);
len = ntohs(udp->uh_ulen) - sizeof(struct udp_hdr);
if (len < sizeof (struct voip_hdr)) {
if (debug > 1)
fprintf(stderr, "Bad packet: Len %d\n", len);
return;
}
voip->id = ntohs(voip->id);
voip->seqnr = ntohl(voip->seqnr);
if (memcmp(voip_magic_data, voip->magic, sizeof(voip_magic_data))) {
if (memcmp(voip_magic_start, voip->magic,
sizeof(voip_magic_start))) {
if (debug > 1)
fprintf(stderr,
"Bad packet: Magic: %02x %02x\n",
voip->magic[0], voip->magic[1]);
return;
}
if (lastidnr != voip->id) {
struct addr src, dst;
lastidnr = voip->id;
addr_pack(&src, ADDR_TYPE_IP, IP_ADDR_BITS,
&ip->ip_src, IP_ADDR_LEN);
addr_pack(&dst, ADDR_TYPE_IP, IP_ADDR_BITS,
&ip->ip_dst, IP_ADDR_LEN);
strlcpy(srcname, addr_ntoa(&src), sizeof (srcname));
strlcpy(dstname, addr_ntoa(&dst), sizeof (dstname));
fprintf(stderr,
"Client: %s -> %s:\n"
"\tid %d, seqnr: %u, 0x%0x\n"
"\tDialed number: %s\n",
srcname, dstname,
voip->id, voip->seqnr,
ntohl(voip->magicnr),
phone_number);
buffer_clear(bhd);
}
}
len -= sizeof (struct voip_hdr);
for (i = 0; i < len; i++) {
buf[i] = ulaw2linear(p[i]);
}
make_ident(&ip->ip_src, udp->uh_sport,
&ip->ip_dst, udp->uh_dport, voip->magicnr,
&ident);
buffer_enqueue(bhd, &ident, voip->id, voip->seqnr, buf, len);
return;
}
void
voip_sniff(u_char *user, const struct pcap_pkthdr *pkthdr, const u_char *pkt)
{
struct ip_hdr *ip;
ip = (struct ip_hdr *)(pkt + pcap_off);
switch(ip->ip_p) {
case IPPROTO_UDP:
voip_sniff_udp(user, pkthdr, pkt);
break;
case IPPROTO_TCP:
voip_sniff_tcp(user, pkthdr, pkt);
break;
default:
break;
}
}
syntax highlighted by Code2HTML, v. 0.9.1