Pedro, esse seu esforço em criar uma documentação para desenvolvedores 
é louvável.
O HLBR é um projeto complexo e se não fosse sua documentação, eu por 
exemplo, demoraria anos para aprender tudo o que você escreveu nesses 
dois guias.
Parabéns cara, sua atitude acaba sendo um convite para novos 
desenvolvedores.

Abraços



--- Em [email protected], PEdroArthur_JEdi <pedro.fo...@...> 
escreveu
>
> Continuando um pouco com a criação de guias para desenvolvedores, 
está
> ai mais um. É um guia prático de criação de decoders. Contém um
> exemplo grosseiro de um decodificador NTP (Network Time Protocol) o
> qual não deve ser usado em ambiente de produção!!!
> 
> Estamos abertos a dúvidas!
> 
> Espero que gostem!
> 
> Feliz ano novo a todos!
> 
> -- 
> PEdroArthur_JEdi
> 
> Nunca acredite num sistema que você não conhece o código fonte!
> Never trust a system you don't have sources for!
> 
> "Só se dedicará a um assunto com toda a seriedade alguém que esteja
> envolvido de modo imediato e que se ocupe dele com amor. É sempre de
> tais pessoas, e não dos assalariados, que vêm as grandes 
descobertas."
> 
> -- Arthur Schopenhauer
> 
> O desenvolvimento de Decoders para o HLBR envolve um simples 
entendimento de como manipular a estrutura de dados DecoderRec, 
definida em engine/hlbr.c e transcrita abaixo:
> 
>       231:    typedef struct decoder_rec{
>       232:            char                    Name[MAX_NAME_LEN];
>       233:            int                     ID;
>       234:            unsigned char           
DependencyMask[MAX_RULES/8];            
>       235:            struct test_rec*        Tests;
>       236:            struct module_rec*      Modules;
>       237:            struct decoder_rec*     Children;
>       238:            struct decoder_rec*     Parent; 
>       239:            struct decoder_rec*     NextChild;
>       241:            void* (*DecodeFunc) (int PacketSlot);
>       242:            void (*Free) (void *pointer);
>       243:            int (*ConfigFunction) (FILE *fp);
>       245:            char                    Active; /*true if 
anything actually uses it*/
>       246:    } DecoderRec;
> 
> O campo char* Name representa o nome do decoder e como ele será 
usado para escrever regras; ID é um identificador único gerado pela 
função CreateDecoder(char *), definida em decoders/decode.c; unsigned 
char* DependencyMask é a máscara de depêndencia do decoder na 
hierarquia, sendo esse parâmetro ajustado automaticamente pela função 
DecoderAddDecoder(int, int), a qual é definida em decoders/decode.c; 
os campos Tests e Module são manipulados automaticamente pelas funções 
DecoderAddTest(int,int) (responsável por associar um teste a um 
decoder) e DecoderAddModule(int,int) (responsável por associar um 
módulo a um decoder), respectivamente, estando ambas declaradas no 
arquivo fonte decoders/decode.c. Children, Parent e NextChild são 
todos manipulados pela função DecoderAddDecoder(int, int), a qual 
gerencia a hierárquia de decoders do HLBR; O campo void* 
(*DecodeFunc)(int) é a principal função de um decoder, estando 
encarregada de realizar a decodificação em si; O campo void 
(*Free)(void*) é a função que liberará qualquer dado que tenha sido 
alocado pelo decoder; int (*ConfigFunction)(FILE *) deve ser usada 
quando um decoder necessita de parâmetros especiais passados pelo 
arquivo de configuração, tal qual o DecoderHTTP.
> 
> Estando explicado os campos do DecoderRec, podemos passar para a 
função de inicialização do decoder. Consideremos um decoder chamado 
NOME, que tem como decoder pai Parent, função de decodificação 
DecodeNOME e gera seus dados através de um malloc simples, o que 
consequentemente torna o uso da função da biblioteca padrão void free 
(void*) viável e não possue parâmetros especiais. O esqueleto desse 
decoder pode ser visto a seguir:
> 
>       1:      int ParentDecoderID;
>       2:
>       3:      int InitDecoderNOME(){
>       4:              int DecoderID;
>       5:
>       6:              DEBUGPATH;
>       7:      
>       8:              if 
((DecoderID=CreateDecoder("NOME"))==DECODER_NONE){
>       9:      #ifdef DEBUG
>       10:                     printf("Couldn't Allocate NOME 
Decoder\n");
>       11:     #endif
>       12:                     return FALSE;
>       13:             }
>       14:             
>       15:             
Globals.Decoders[DecoderID].DecodeFunc=DecodeNOME;
>       16:             Globals.Decoders[DecoderID].Free=free;
>       17:             if 
(!DecoderAddDecoder(GetDecoderByName("Parent"), DecoderID)){
>       18:                     printf("Failed to Bind TCP Decoder to 
Parent Decoder\n");
>       19:                     return FALSE;
>       20:             }
>       21:
>       22:             ParentDecoderID=GetDecoderByName("Parent");
>       23:
>       24:             return TRUE;
>       25:     }
> 
> Por convenção e para facilitar o entendimento, usa-se no HLBR como 
nome da função de inicialização de um decoder InitDecoderNOME, onde 
NOME é substituido pelo nome do decoder, ex: InitDecoderTCP, 
InitDecoderUDP. A essa função é encarregada a missão de alocar espaço 
para o decoder, configurar sua função de decodificação, função de 
liberação de dados e sua posição na hierárquia.
> 
> A alocação é feita pela função int CreateDecoder(char *), definida 
em decoder/decode.c. Ela fará o trabalho de registrar o decoder 
globalmente, incuindo-o na próxima posição livre de Globals.Decoders e 
configurando seu nome no campo char* Name. O valor de retorno de 
CreateDecoder será o identificador único do decoder e deverá ser usado 
para manipulá-lo na hierarquia de decoders.
> 
> Logo em seguida, deve-se associar o recém criado decoder ao seu pai, 
aqui referenciado por "Parent". DecoderAddDecoder(int,int) irá 
associar o decoder de ID representado pelo seu segundo parâmetro ao ID 
do decoder do primeiro parâmetro, onde o segundo será filho do 
primeiro, sendo os campos Parent e Children atualizados. Para auxiliar 
nessa tarefa, a função int GetDecoderByName(char *) retorna o ID do 
decoder de nome igual ao seu parâmetro, sendo desconsiderado a caixa, 
ex: TCP == tcp == Tcp == tCp == tcP, e por ai vai... Caso essa tarefa 
ocorra com sucesso, será retornado TRUE, caso contrário será retornado 
FALSE. No código acima tivemos o cuidado de colocar uma estrutura de 
controle para que o fluxo seja interrompido caso não seja possível 
associar os decoders, linhas 17 a 20.
> 
> Outro ponto importante é associar as funções de decodificação e 
liberação. Simplesmente, deve-se colocar os devidos valores nos campos 
DecodeFunc e Free. Essa tarefa está representada nas linhas 15 e 16, 
respectivamente.
> 
> Por ultimo, tem se mostrado interessante guardar o ID do decoder pai 
para que se possa utiliza-lo posteriormente. Por convenção (e 
simplificação), é usado uma variável global ao escopo do código 
nomeada seguindo o padrão NOMEDecoderID, onde NOME deve ser 
substituido pelo nome do decoder pai, ex: TCPDecoderID, UDPDecoderID.
> 
> Para que tudo ocorra bem, devemos retornar TRUE :)
> 
> Vamos prosegeuir agora com a criação de uma pequena função de 
decodificação. As funções de decodificação devem retornar um ponteiro 
do tipo void, onde valores diferentes de nulo representam que a 
decodificação ocorreu sem problemas e o HLBR deve prosegeuir com os 
testes desse protocolo. Caso contrário, nulo fará com que a função 
DecodePacket (int) passe para o próximo decoder. Como exemplo, vamos 
criar um decoder que identifique a possível presença de um payload 
NTP. A função se limitará apenas a checar se o primeiro byte se 
enquadra como um payload NTP. Seu código encontra-se abaixo.
> 
>               /* * * * * * * * *
>               *
>               * decode_ntp.h
>               *
>               * * * * * * * * */
> 
>       1:      #ifndef _HLBR_DECODE_NTP_H_
>       2:      #define #ifndef _HLBR_DECODE_NTP_H_
>       3:
>       4:      typedef struct ntpv {
>       5:              unsigned char   li:2,
>       6:                              ver:3,
>       7:                              mode:3;
>       8:      } NTPVer;
>       9:
>       10:     typedef struct ntpd {
>       11:             char *BeginData;
>       12:             NTPVer Header;
>       13:     } NTPData;
>       14:
>       15:     int InitDecoderNTP ();
>       16:
>       17:     #endif
> 
>               /* * * * * * * * *
>               *
>               * decode_ntp.c
>               *
>               * * * * * * * * */
> 
>       1:      #include "decode_ntp.h"
>       2:      #include "../config.h"
>       3:      #include "../packets/packet.h"
>       4:      #include "decode_udp.h"
>       5:
>       6:      #include <stdio.h>
>       7:      #include <stdlib.h>
>       8:
>       9:      int UDPDecoderID;
>       10:
>       11:     int DecodeNTP (int PacketSlot) {
>       12:             UDPData*        udpData;
>       13:             PacketRec*      p;
>       14:             NTPData*        data;
>       15:
>       16:             DEBUGPATH;
>       17:
>       18:             if (!GetDataByID(PacketSlot, UDPDecoderID, 
(void**)&udpData)) {
>       19:                     printf("Failed to get UDP data\n");
>       20:                     return NULL;
>       21:             }
>       22:
>       23:             p = &Globals.Packets[PacketSlot];
>       24:
>       25:             if (((p->BeginData[0] & 0x38) >> 3) < 3 || 
((p->BeginData[0] & 0x38) >> 3) > 4) {
>       26:                     return NULL;
>       27:             }
>       28:
>       29:             data = (NTPData *) malloc (sizeof(NTPData));
>       30:
>       31:             data->Header = (NTPVer) p->BeginData[0];
>       32:             data->BeginData = &p->BeginData[1];
>       33:
>       34:             return data;
>       35:     }
>       36:
>       37:     int InitDecoderNTP () {
>       38:             int DecoderID;
>       39:
>       40:             DEBUGPATH;
>       41:
>       42:             if ((DecoderID=CreateDecoder("NTP")) == 
DECODER_NONE) {
>       43:     #ifdef DEBUG
>       44:                     printf ("Couldn't Allocate NTP 
Decoder\n");
>       45:     #endif
>       46:                     return FALSE;
>       47:             }
>       48:
>       49:             
Globals.Decoders[DecoderID].DecodeFunc=DecodeNTP;
>       50:             Globals.Decoders[DecoderID].Free=free;
>       51:
>       52:             if 
(!DecoderAddDecoder(GetDecoderByName("UDP"), DecoderID)){
>       53:                     printf("Failed to Bind NTP Decoder to 
UDP Decoder\n");
>       54:                     return FALSE;
>       55:             }
>       56:
>       57:             UDPDecoderID=GetDecoderByName("UDP");
>       58:
>       59:             return TRUE;
>       60:     }
> 
>       OBS: Esse código não foi testado, nem compilado. Não integrar 
de maneira alguma a um servidor em produção!!!
>       OBS: Porém, caso queira apenas aprender como escrever um 
decoder e integrar ao código do HLBR, faça-o!
> 
> Os decoders sempre são compostos por dois arquivos, o primeiro 
contento as estruturas de dados, as funções e as variáveis que poderão 
ser usadas em outras partes do código, tipo testes e módulos, e o 
segundo a implementação do decoder. No primeiro arquivo, decode_ntp.h, 
deve-se incluir uma diretiva de processamento para evitar que seu 
conteúdo seja incluído mais de uma vez:
> 
>       1:      #ifndef _HLBR_DECODE_NTP_H
>       2:      #define #ifndef _HLBR_DECODE_NTP_H
> 
>               /* Code Code Code */
> 
>       3:      #endif
> 
> A convenção usada é _HLBR_DECODE_NOME_H_ , onde NOME deve ser 
substituído pelo nome do decoder.
> 
> No caso do exemplo, declaramos a estrutura de dados NTPData, que 
servirá para guardar as informações decodificadas, NTPVer, que servirá 
para guardar informações sobre a versão do cabeçalho NTP, e a função 
int InitDecoderNTP(), a qual será usada para registrar o decoder NTP 
no sistema.
> 
> A função DecodeNTP simplesmente verifica se a versão é válida, 
linhas 25 e 26, e caso seja, aloca uma estrutura NTPData, linha 29, 
guarda as informações relevantes, linhas 31 e 32, e retorna os dados.
> 
> Para verificar o payload do pacote, primeiro precisamos "pedir" os 
dados ao decoder específico. Aqui no caso, o UDP. A função GetDataByID 
(int,int,void**) recebe três parâmetros, sendo o primeiro o número do 
pacote decodificado, o decoder do qual se deseja a saída e um ponteiro 
para ponteiro nulo, no qual será retornado os dados. O descrito 
anteriormente ocorre nas linhas 18 a 21.
> 
>       OBS: Para um exemplo onde se tem uma função de inicialização e 
uma função de liberação veja decoders/decode_http.c.
> 
> Feito os códigos fontes, a próxima etapa consiste em editar os 
arquivos de Makefile para que o código seja compilado. Os arquivos a 
serem editados são o Makefile.in e o decoders/Makefile. Em Makefile 
deve ser adicionar o caminho do código em HLBR_OBJECTS:
> 
>       HLBR_OBJECTS= \
>               engine/hlbr.o \
>               engine/alert_limit.o \
>               engine/bits.o \
> 
>               /* Mais Alguns */
> 
>               decoders/decode.o \
>               decoders/decode_interface.o \
>               decoders/decode_ethernet.o \
>               decoders/decode_ip.o \
>               decoders/decode_ip_defrag.o \
>               decoders/decode_icmp.o \
>               decoders/decode_udp.o \
>               decoders/decode_tcp.o \
>               decoders/decode_http.o \
>               decoders/decode_dns.o \
>               decoders/decode_arp.o \
>               decoders/decode_ntp.o \
> 
>               /* continua */
> 
> Já no decoders/Makefile, deve adicionar a ação all e criar as regras 
do objeto:
> 
>       all: decode.o \
>       decode_interface.o \
>       decode_ethernet.o \
>       decode_ip.o \
>       decode_ip_defrag.o \
>       decode_icmp.o \
>       decode_udp.o \
>       decode_tcp.o \
>       decode_http.o \
>       decode_dns.o \
>       decode_arp.o \
>       decode_ntp.o
> 
>       /* algumas regras */
> 
>       decode_ntp.o: decode_ntp.c decode_ntp.h ../config.h 
../engine/hlbr.h
>               $(CC) -c -o decode_ntp.o decode_ntp.c $(DEBUG)
> 
> Agora, o processo mais angustiante:
> 
>       $ ./configure
>       $ make
>       /* Algumas Linhas */
> 
>       gcc -g    -c -o decoders/decode.o decoders/decode.c
>       gcc -g    -c -o decoders/decode_interface.o 
decoders/decode_interface.c
>       gcc -g    -c -o decoders/decode_ethernet.o 
decoders/decode_ethernet.c
>       gcc -g    -c -o decoders/decode_ip.o decoders/decode_ip.c
>       gcc -g    -c -o decoders/decode_ip_defrag.o 
decoders/decode_ip_defrag.c
>       gcc -g    -c -o decoders/decode_icmp.o decoders/decode_icmp.c
>       gcc -g    -c -o decoders/decode_udp.o decoders/decode_udp.c
>       gcc -g    -c -o decoders/decode_tcp.o decoders/decode_tcp.c
>       gcc -g    -c -o decoders/decode_http.o decoders/decode_http.c
>       gcc -g    -c -o decoders/decode_dns.o decoders/decode_dns.c
>       gcc -g    -c -o decoders/decode_arp.o decoders/decode_arp.c
>       gcc -g    -c -o decodets/decode_ntp.o decoders/decode_ntp.c
> 
>       /* mais linhas */
> 
>       gcc -g -o hlbr -g -Wall engine/hlbr.o engine/alert_limit.o 
engine/bits.o engine/parse_config.o engine/parse_rules.o 
engine/main_loop.o engine/session.o engine/jtree.o engine/num_list.o 
engine/message.o engine/cache.o engine/regex.o engine/hlbrlib.o 
engine/url.o engine/logfile.o packets/packet.o 
packets/packet_linux_raw.o packets/packet_obsd_bpf.o 
packets/packet_osx_bpf.o packets/packet_tcpdump.o 
packets/packet_solaris_dlpi.o decoders/decode.o 
decoders/decode_interface.o decoders/decode_ethernet.o 
decoders/decode_ip.o decoders/decode_ip_defrag.o 
decoders/decode_icmp.o decoders/decode_udp.o decoders/decode_tcp.o 
decoders/decode_http.o decoders/decode_dns.o decoders/decode_arp.o 
decoders/decode_ntp.o tests/test.o tests/test_interface_name.o 
tests/test_ethernet_type.o tests/test_ethernet_src.o 
tests/test_ethernet_dst.o tests/test_ip_src.o tests/test_ip_dst.o 
tests/test_ip_proto.o tests/test_ip_ttl.o tests/test_ip_check.o 
tests/test_icmp_type.o tests/test_icmp_code.o tests/test_tcp_port.o 
tests/test_tcp_src.o tests/test_tcp_dst.o tests/test_tcp_content.o 
tests/test_tcp_nocase.o tests/test_tcp_listcontent.o 
tests/test_tcp_listnocase.o tests/test_tcp_flags.o 
tests/test_tcp_offset.o tests/test_tcp_regex.o 
tests/test_http_content.o tests/test_http_method.o 
tests/test_http_nocase.o tests/test_http_regex.o 
tests/test_udp_regex.o tests/test_udp_src.o tests/test_udp_dst.o 
tests/test_udp_content.o tests/test_udp_nocase.o actions/action.o 
actions/action_drop.o actions/action_alert_console.o 
actions/action_alert_file.o actions/action_dump_packet.o 
actions/action_route_sip.o actions/action_bns.o 
actions/action_alert_syslog.o actions/action_alert_email.o 
actions/action_alert_socket.o actions/action_alert_listensocket.o 
routes/route.o routes/route_macfilter.o routes/route_simple_bridge.o 
routes/route_dip.o routes/route_sip.o routes/route_broadcast.o 
routes/route_arp.o routes/route_interface.o routes/route_bns.o -
lpthread -ldl -lpcre -rdynamic
>       #
>       # ---------------------------------------
>       # Execute "# make install" para instalar.
>       # Run "# make install" to install.
>       # ---------------------------------------
>       #
> 
> ** Dicas **
> 
> Apesar de simples, a criação de um decoder demanda tempo e estudo. 
Devemos pensar primeiramente no custo benefício do mesmo. 
Primeiramente, o custo está relacionado a performance. Deve-se 
escolher algoritmos eficientes, evitar alocação dinâmica ao máximo, 
evitar memory leaks, controlar o acesso concorrente aos dados, em fim, 
tudo!
>



Responder a