All pastes #959977 Raw Edit

Something

public text v1 · immutable
#959977 ·published 2008-03-27 20:20 UTC
rendered paste body

/*
        VoIPong Voice Over IP Sniffer
        Copyright (C) 2004 Murat Balaban <murat || enderunix.org>
        All rights reserved.

        This program 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
        of the License, or (at your option) any later version.

        This program 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 General Public License for more details.

        You should have received a copy of the GNU General Public License
        along with this program; if not, write to the Free Software
        Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/


#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <netdb.h>
#include <syslog.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>

#include <miscutil.h>
#include <conf.h>

#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/un.h>

#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <arpa/inet.h>

#include <netinet/ip.h>
#include <netinet/udp.h>

#include <g711.h>
#include <voipongsign.h>
#include <voipongpcap.h>
#include <voipongmgmt.h>
#include <voipongworker.h>
#include <voipongcdr.h>
#include <voipongcodec.h>

extern char goutdir[256];
extern char gsoxpath[256];
extern char gsoxmixpath[256];
extern int grtp_idle_time;
extern int gsoxmixflag;
extern vocoder vocoders[MAXCODECS];




worker *workers[MAXNODE];
worker *curw = NULL;
pid_t mypid = -1;

static char *rtp_pt_desc[] = {"0-PCMU-8KHz",
        "1-1016-8KHz",
        "2-G726-32-8KHz",
        "3-GSM-8KHz",
        "4-G723.1-8KHz",
        "5-DVI4-8KHz",
        "6-DVI4-16KHz",
        "7-LPC-8KHz",
        "8-PCMA-8KHz",
        "9-G722-8KHz",
        "10-L16-44.1KHz",
        "11-L16-44.1KHz",
        "12-QCELP-8KHz",
        "13-CN-8KHz",
        "14-MPA-90KHz",
        "15-G728-8KHz",
        "16-DVI4-11KHz",
        "17-DVI4-22KHz",
        "18-G729-8KHz",
        "19-reserved",
        "20-unassigned",
        "21-unassigned",
        "22-unassigned",
        "23-unassigned",
        "24-unassigned",
        "25-CelB-90KHz",
        "26-JPEG-90KHz",
        "27-unassigned"
};

int
create_wave()
{
        char outf1[1024];
        char outf2[1024];
        char mixout[1024];
        char inrate[32];
        char insize[4];
        char indenc[4];
        struct stat st;
        int outcnt = 0;


        if (vocoders[curw->rtp->enc].f == vocoder_default) {
                misc_debug(0, "[%d] create_wave: no modules have been loaded to decode payload type: %d\n", getpid(), curw->rtp->enc);
                misc_debug(0, "[%d] create_wave: leaving *.raw files untouched.\n", getpid());
                return -1;
        }
        snprintf(inrate, sizeof(inrate) - 1, "%d", vocoders[curw->rtp->enc].rate);
        strcpy(insize, "-w");
        strcpy(indenc, "-s");

        strncpy(outf1, curw->file1name, sizeof(outf1) - 2);
        memcpy(outf1 + (strlen(outf1) - 4), ".wav", 4);
        if (stat(curw->file1name, &st) == 0) {
                if (exec_sox(inrate, insize, indenc, curw->file1name, outf1) == 0) {
                        if (unlink(curw->file1name) == -1)
                                misc_debug(0, "[%d] Cannot remove file [%s]: %s\n", mypid, curw->file1name, strerror(errno));
                } else {
                        misc_debug(0, "[%d] exec_sox failed!\n", mypid);
                        return -1;
                }
                misc_debug(0, "[%d] .WAV file %s has been created successfully\n", mypid, outf1);
                outcnt++;
        }

        strncpy(outf2, curw->file2name, sizeof(outf2) - 2);
        memcpy(outf2 + (strlen(outf2) - 4), ".wav", 4);
        if (stat(curw->file2name, &st) == 0) {
                if (exec_sox(inrate, insize, indenc, curw->file2name, outf2) == 0) {
                        if (unlink(curw->file2name) == -1)
                                misc_debug(0, "[%d] Cannot remove file [%s]: %s\n", mypid, curw->file2name, strerror(errno));
                } else {
                        misc_debug(0, "[%d] exec_sox failed!\n", mypid);
                        return -1;
                }
                misc_debug(0, "[%d] .WAV file %s has been created successfully\n", mypid, outf2);
                outcnt++;
        }
        if (gsoxmixflag && (outcnt > 1)) {
                strncpy(mixout, curw->file1name, sizeof(mixout) - 13);
                strcpy(mixout + (strlen(mixout) - 4), "-mixed.wav");
                if (exec_soxmix(outf1, outf2, mixout) == 0) {
                        misc_debug(0, "[%d] Mixed output files: %s\n", mypid, mixout);
                        /*
                        if(unlink(outf1) == -1)
                                misc_debug(0, "[%d] Cannot remove file [%s]: %s\n", mypid, outf1, strerror(errno));
                        if(unlink(outf2) == -1)
                                misc_debug(0, "[%d] Cannot remove file [%s]: %s\n", mypid, outf2, strerror(errno));
                                */
                } else {
                        misc_debug(0, "[%d] Could not mix output files: exec_soxmix failed!\n", mypid);
                }

        }
        return 0;
}

int
exec_sox(char *rt, char *sz, char *dc, char *srcfile, char *dstfile)
{
        pid_t pid, wpid;
        int stat = 0;
        int exitcode = 0;

        switch(pid = fork())  {
                case -1:
                        misc_debug(0, "[%d] exec_sox: cannot fork new process to create .WAV file: %s\n", mypid, strerror(errno));
                        return -1;
                        break;
                case 0:
                        if (execl(gsoxpath, "sox", "-r", rt, sz, dc, srcfile, dstfile, NULL) == -1) {
                                misc_debug(0, "[%d] execl(%s -r %s %s %s %s %s) error: %s\n", mypid, gsoxpath, rt, sz, dc, srcfile, dstfile, strerror(errno));
                                exit(100);
                        }
                        exit(1);
                        break;
                default:
                        if ((wpid = waitpid(pid, &stat, 0)) == -1) {
                                misc_debug(0, "[%d] exec_sox: waitpid: %s\n", mypid, strerror(errno));
                                return -1;
                        }
                        if ((exitcode = WEXITSTATUS(stat)) != 0) {
                                misc_debug(0, "[%d] exec_sox: sox helper process failed, return value: %d\n", mypid, exitcode);
                                return -1;
                        } else
                        if (WIFSIGNALED(stat)) {
                                misc_debug(0, "[%d] exec_sox: sox helper process recv'd siganal %d\n", mypid, pid, WTERMSIG(stat));
                                return -1;
                        }
                        break;
        }
        return 0;
}

int
exec_soxmix(char *f1, char *f2, char *dstfile)
{
        pid_t pid, wpid;
        int stat = 0;
        int exitcode = 0;

        switch(pid = fork())  {
                case -1:
                        misc_debug(0, "[%d] exec_soxmix: cannot fork new process to create mixed .WAV file: %s\n", mypid, strerror(errno));
                        return -1;
                        break;
                case 0:
                        if (execl(gsoxmixpath, "soxmix", f1, f2, dstfile, NULL) == -1) {
                                misc_debug(0, "[%d] execl(%s %s %s %s) error: %s\n", mypid, gsoxmixpath, f1, f2, dstfile, strerror(errno));
                                exit(100);
                        }
                        exit(1);
                        break;
                default:
                        if ((wpid = waitpid(pid, &stat, 0)) == -1) {
                                misc_debug(0, "[%d] exec_soxmix: waitpid: %s\n", mypid, strerror(errno));
                                return -1;
                        }
                        if ((exitcode = WEXITSTATUS(stat)) != 0) {
                                misc_debug(0, "[%d] exec_soxmix: soxmix helper process failed, return value: %d\n", mypid, exitcode);
                                return -1;
                        } else
                        if (WIFSIGNALED(stat)) {
                                misc_debug(0, "[%d] exec_soxmix: soxmix helper process recv'd siganal %d\n", mypid, pid, WTERMSIG(stat));
                                return -1;
                        }
                        break;
        }
        return 0;
}





void
init_workers()
{
        int i = 0;

        for (i = 0; i < MAXNODE; i++)
                workers[i] = NULL;
}

int
worker_create(worker **w, int pllen, u_char *packet)
{
        int h = 0;

        switch(((*w)->pid = fork())) {
                case -1:
                        misc_debug(0, "worker_create: fork: %s\n", strerror(errno));
                        break;
                case 0:
                        worker_main(*w, pllen, packet);
                        exit(0);
                default:
                        time(&(*w)->rtp->stime);
                        h = tuplehash((*w)->rtp->ip1, (*w)->rtp->ip2, (*w)->rtp->port1, (*w)->rtp->port2);
                        (*w)->next = workers[h];
                        workers[h] = (*w);
                        break;
        }
        return (*w)->pid;
}

worker *
getworkerbypid(pid_t pid)
{
        worker *w;
        int i;

        for (i = 0; i < MAXNODE; i++)
                for (w = workers[i]; w != NULL; w = w->next)
                        if (w->pid == pid)
                                return w;

        return NULL;
}

int
worker_isexist(u_int32_t ip1, u_int32_t ip2, u_int16_t port1, u_int16_t port2)
{
        worker *w;
        int h;

        h = tuplehash(ip1, ip2, port1, port2);
        for (w = workers[h]; w != NULL; w = w->next)
                if ((ip1 == w->rtp->ip1 || ip1 == w->rtp->ip2) &&
                                (ip2 == w->rtp->ip1 || ip2 == w->rtp->ip2) &&
                                (port1 == w->rtp->port1 || port1 == w->rtp->port2) &&
                                (port2 == w->rtp->port1 || port2 == w->rtp->port2))
                        return 1;
        return 0;
}

void
worker_remove(worker *in)
{
        worker *w;
        worker *prev = NULL;
        int h = 0;

        /* Remove related RTCP session */
        removertcp(in->rtcp);

        h = tuplehash(in->rtp->ip1, in->rtp->ip2, in->rtp->port1, in->rtp->port2);
        for (w = workers[h]; w != NULL; w = w->next) {
                if (in->pid == w->pid) {
                        if (prev == NULL)
                                workers[h] = w->next;
                        else
                                prev->next = w->next;
                }
                prev = w;
        }
}

void
kill_workers()
{
        worker *w = NULL;
        int i = 0;

        for (i = 0; i < MAXNODE; i++)
                for (w = workers[i]; w != NULL; w = w->next)
                        if (w->pid > 1) {
                                misc_debug(0, "Terminating session [%d]\n", w->pid);
                                kill(w->pid, SIGTERM);
                        }
}

void
worker_main(worker *w, int pllen, u_char *payload)
{
        char filterstr[512];
        char errbuf[ERRBUFSIZ];
        char strpt[32];
        char tfmt[64];
        char strip1[32];
        char strip2[32];
        int pd = -1;
        pcap_t *pcapptr;
        fd_set set;
        time_t tnow, tidle;
        struct timeval tv;
        struct sigaction sa, sa1;
        struct sigaction sa_old, sa_old1;

        sa.sa_handler = sigworkerhandler;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = SA_SIGINFO;
        sigaction(SIGHUP, &sa, &sa_old);
        sigaction(SIGUSR2, &sa, &sa_old);
        sigaction(SIGTERM, &sa, &sa_old);
        sigaction(SIGINT, &sa, &sa_old);
        sigaction(SIGSTOP, &sa, &sa_old);
        sigaction(SIGQUIT, &sa, &sa_old);
        sigaction(SIGALRM, &sa, &sa_old);
        sa.sa_handler = SIG_IGN;
        sigemptyset(&sa1.sa_mask);
        sigaction(SIGCHLD, &sa1, &sa_old1);

        curw = w;
        mypid = getpid();
        misc_strftime(tfmt, sizeof(tfmt) - 2, "%Y%m%d");
        snprintf(strip1, sizeof(strip1) - 2, misc_inet_ntoa(w->rtp->ip1));
        snprintf(strip2, sizeof(strip2) - 2, misc_inet_ntoa(w->rtp->ip2));
        if (create_outpath() == -1) {
                misc_debug(0, "[%d] create_path failed!\n", mypid);
                exit(1);
        }
        if (w->rtp->enc > MAXCODECS) {
                misc_debug(0, "Encoding type %d is higher than MAXCODECS value [%d], giving up...\n", w->rtp->enc, MAXCODECS);
                exit(0);
        }
        if (w->rtp->enc > 27)
                snprintf(strpt, sizeof(strpt) - 2, "%d-unknown", w->rtp->enc);
        else
                snprintf(strpt, sizeof(strpt), "%s", rtp_pt_desc[w->rtp->enc]);

        snprintf(w->file1name, MAXLBUFSIZ - 2, "%s/%s/session-enc%s-%s,%d-%s,%d.raw", goutdir, tfmt, strpt, 
                        strip1, w->rtp->port1, strip2, w->rtp->port2);
        if ((w->ip1fd = creat(w->file1name, S_IRWXU)) == -1) {
                misc_debug(0, "[%d] Cannot create raw voice output file %s: %s\n", mypid, w->file1name, strerror(errno));
                exit(1);
        }
        snprintf(w->file2name, MAXLBUFSIZ - 2, "%s/%s/session-enc%s-%s,%d-%s,%d.raw", goutdir, tfmt, strpt, 
                        strip2, w->rtp->port2, strip1, w->rtp->port1);
        if ((w->ip2fd = creat(w->file2name, S_IRWXU)) == -1) {
                misc_debug(0, "[%d] Cannot create raw voice output file %s: %s\n", mypid, w->file2name, strerror(errno));
                exit(1);
        }
        switch(w->rtp->enc) {
                case PT_ULAW:
                        w->rtp->rate = 8000;
                        break;
                default:
                        w->rtp->rate = 0;
                        break;
        }
        misc_debug(0, "[%d] VoIP call has been detected.\n", mypid);
        misc_debug(0, "[%d] %s:%d <--> %s:%d\n", mypid, strip1, w->rtp->port1, strip2, w->rtp->port2);
        misc_debug(0, "[%d] Encoding %s, recording.......\n", mypid, strpt);

        /* Now, let's talk real bussiness ! */
        /* First RTP packet, inherited from parent process */
        vocoders[curw->rtp->enc].f (w->ip1fd, payload, pllen);

        /* Reopen pcap interface for a fixed port tuple ! */
        snprintf(filterstr, sizeof(filterstr) - 2, "(host %s and port %d) and (host %s and port %d) and udp",
                        strip1, w->rtp->port1, strip2, w->rtp->port2);
        if ((pd = initpcap(0, filterstr, errbuf)) == -1) {
                misc_debug(0, "[%d] initpcap: %s\n", mypid, errbuf);
                exit(1);
        }
        pcapptr = getpcapt();

        tv.tv_sec = grtp_idle_time;
        tv.tv_usec = 0;
        time(&tnow);
        tidle = tnow;
        for ( ; ; ) {
                FD_ZERO(&set);
                FD_SET(pd, &set);
                switch(select(pd + 1, &set, NULL, NULL, &tv)) {
                        case -1:
                                misc_debug(0, "[%d] select: error: %s\n", mypid, strerror(errno));
                                exit(1);
                                break;
                        case 0:
                                time(&tnow);
                                if ((tnow - tidle) > grtp_idle_time) {
                                        misc_debug(0, "[%d] maximum idle time [%d secs] has been elapsed for this call, the call might have been ended.\n", mypid,
                                                        grtp_idle_time);
                                        w->etime = time(&w->etime) - grtp_idle_time;
                                        if (create_wave() == 0) {
                                                unlink(curw->file1name);
                                                unlink(curw->file2name);
                                        }
                                        worker_graceful_exit(0);
                                }
                                break;
                        default:
                                time(&tidle);
                                peekpcap(-1, dumprtppayload);
                                break;
                }
        }
        exit(0);
}

void
dumprtppayload(u_char *udata, const struct pcap_pkthdr *pkthdr, const u_char *packet)
{
        u_char *pl;
        int plen = 0;
        struct ip *ip;
        int fd = -1;
        int off = 0;
        int iphlen = 0;
        unsigned short padlen = 0;
        struct rtphdr *rtp;
        unsigned short cur_seq = 0;
        static unsigned short last_seq1 = 0, last_seq2 = 0;

        off = getdllen();
        ip = (struct ip *)(packet + off);
        iphlen = ip->ip_hl << 2;
        rtp = (struct rtphdr *)(packet + off + iphlen + UDP_L);
        pl = (u_char *)(packet + off + iphlen + UDP_L + RTP_L);
        plen = pkthdr->len - (getdllen() + iphlen + UDP_L + RTP_L);
        if (rtp->pad) { /* If padding is enabled, calc real payload length */
                padlen = (unsigned short)(pl[plen]);
                plen -= padlen;
        }
        /*
        printf("\n[-------- START RTP HEADER ----------]\n");
        printf("Version   : %d\n", rtp->ver);
        printf("Padding   : %d\n", rtp->pad);
        printf("Extension : %d\n", rtp->ext);
        printf("CSRC count: %d\n", rtp->cc);
        printf("Mark      : %d\n", rtp->mark);
        printf("Payload   : %d\n", rtp->pt);
        printf("SeqNo     : %d\n", rtp->seq);
        printf("Timestamp : %d\n", rtp->timestamp);
        printf("SSRC      : %d\n", rtp->ssrc);
        printf("CSRC      : %d\n", rtp->csrc);
        printf("[---------- END RTP HEADER ----------]\n");
        */
        /* Avoid duplicate packets */
        cur_seq = ntohs(rtp->seq);
        if (cur_seq == last_seq1 || cur_seq == last_seq2)
                return;
        if (ip->ip_src.s_addr == curw->rtp->ip1) {
                fd = curw->ip1fd;
                last_seq1 = cur_seq;
        } else {
                fd = curw->ip2fd;
                last_seq2 = cur_seq;
        }
        vocoders[curw->rtp->enc].f (fd, pl, plen);
}

int
create_outpath()
{
        char tfmt[64];
        char filefmt[512];
        struct stat st;

        if (stat(goutdir, &st) == -1)
                if (errno == ENOENT) {
                        if (mkdir(goutdir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) {
                                misc_debug(0, "cannot create outputdir %s: %s\n", goutdir, strerror(errno));
                                return -1;
                        }
                }

        misc_strftime(tfmt, sizeof(tfmt) - 1, "%Y%m%d");
        snprintf(filefmt, sizeof(filefmt) - 2, "%s/%s", goutdir, tfmt);

        if (stat(filefmt, &st) == -1)
                if (errno == ENOENT) {
                        if (mkdir(filefmt, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) {
                                misc_debug(0, "cannot create daily outputdir %s: %s\n", filefmt, strerror(errno));
                                return -1;
                        }
                }
        return 0;
}

void
worker_graceful_exit(int code)
{
        if (code == 0) 
                add2cdr(curw);
        exit(code);
}