Сам себе IP-телефон. VoIP для программера
Салиев Александр aka Fagot
Xakep, номер #056, стр. 056-062-3
LibPcap - ядро снифера
Особо терпеливые могут попробовать писать с помощью raw-сокетов, но я предлагаю использовать универсальную библиотеку libpcap, что помимо облегчения в области программирования даст нам еще и возможность использовать правила фильтрации, наподобие тех, что пользуют в tcpdump'е. Структура приложения вырисовывается приблизительно следующая: блок инициализации, настраивающий и запускающий основной цикл libpcap'а, пакетный диспетчер (функция, которая будет вызываться на каждый отловленный пакет, такова архитектура libpcap), и непосредственно функции декодировки H.323, через которые мы будем отлавливать необходимую инфу. Произведя все необходимые настройки библиотеки, запустим основной цикл pcap-ного снифера, указав ему в качестве диспетчера функцию void dispatcher_handler (u_char *,const struct pcap_pkthdr *, const u_char *):
pcap_loop(fp, 0, dispatcher_handler, (unsigned char *)NULL);
Это ядро нашего снифера, которое будет отдавать каждый пойманный и прошедший фильтрацию пакет в функцию dispatcher_handler. Задача диспетчера - разобрать, стоит ли обрабатывать данный пакет, освободить его от мусора и отдать декодеру:
Основной цикл
void dispatcher_handler(u_char *dumpfile, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
...
Произведем отсечение не ethernet-пакетов, не IP-пакетов, и, наконец, доберемся до UDP и TCP
...
if (ip->ip_p == 0x06) /* Это TCP */
{
tcp = (struct tcphdr *) (pkt_data + ETH_H + IP_H);
int datalen = ntohs(ip->ip_len)-TCP_H-IP_H;
data = (char *) (pkt_data + ETH_H + IP_H + TCP_H);
if (datalen>4 && data[0]==0x03 && data[1]==0x00 ) /* TCP-пакеты, не являющиеся TPKT3, нам не нужны */
{
Если TCP-пакет имеет TPKT3-заголовок, разумно предположить, что это H.225-пакет, и попытаться его раскодировать:
tcpdecodeh323(data, datalen);
}
}
if (ip->ip_p == 0x11) /*Это UDP*/
{
udp = (struct udphdr *) (pkt_data + ETH_H + IP_H);
int datalen = ntohs(ip->ip_len)-UDP_H-IP_H;
data = (char *) (pkt_data + ETH_H + IP_H + UDP_H);
if (datalen>0 ) /* В UDP-пакетах нет TPKT3, но нулевые пакеты нам не нужны */
{
RAS-пакеты не имеют контрольных заголовков, поэтому придется пробовать "на авось" каждый пакет:
udpdecodeh323(data, datalen);
}
}
}
Ради чего все замышлялось
Теперь, когда мы контролируем пакеты в сети и разложили их по кучкам, нам надо создать собственно сами функции декодирования. Функция tcpdecodeh323(char * data, int datalen) будет пытаться раскодировать TCP-пакет, исходя из предположения, что это TCP-TPKT3-Q.931-H.323-H.225-SETUP. А функция udpdecodeh323(char * data, int datalen) - исходя из предположения, что это UDP-H.323-H.225RAS-ADMISSIONREQUEST. Для этого мы как раз и задействуем библиотеку OpenH323: