[Project] 프로젝트 pcap 라이브러리 도메인 차단
카테고리: Project
코드
#include <stdio.h>
#include <pcap.h>
#include <string.h>
#include <stdlib.h>
#define ETHER_ADDR_LEN 6
struct sniff_ethernet {
u_char ether_dhost[ETHER_ADDR_LEN];
u_char ether_shost[ETHER_ADDR_LEN];
u_short ether_type;
};
struct sniff_ip {
u_char ip_vhl;
u_char ip_tos;
u_short ip_len;
u_short ip_id;
u_short ip_off;
#define IP_RF 0x8000
#define IP_DF 0x4000
#define IP_MF 0x2000
#define IP_OFFMASK 0x1fff
u_char ip_ttl;
u_char ip_p;
u_short ip_sum;
struct in_addr ip_src, ip_dst;
};
#define IP_HL(ip) (( (ip)->ip_vhl ) & 0x0f)
#define IP_V(ip) (( (ip)->ip_vhl ) >> 4)
typedef u_int tcp_seq;
struct sniff_tcp {
u_short th_dport;
u_short th_sport;
tcp_seq th_seq;
tcp_seq th_ack;
u_char th_offx2;
#define TH_OFF(tcp) (( (tcp)->th_offx2 & 0xf0) >> 4 )
u_char th_flags;
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
#define TH_ECE 0x40
#define TH_CRW 0x80
#define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG|TH_ECE|TH_CRW)
u_short th_win;
u_short th_sum;
u_short th_urp;
};
void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char* packet);
int main( int argc, char *argv[])
{
char *dev, errbuf[PCAP_ERRBUF_SIZE];
pcap_t *handle;
bpf_u_int32 net;
bpf_u_int32 mask;
struct bpf_program fp;
char filter_exp[] = "port 80";
struct pcap_pkthdr header;
const u_char *packet;
dev = pcap_lookupdev(errbuf);
if( dev == NULL ) {
fprintf(stderr, "could not find default device %s \n", errbuf);
return 2;
}
if( pcap_lookupnet(dev, &net, &mask, errbuf) == -1 ) {
fprintf(stderr, "could not get netmask for device %s : %s \n", dev, errbuf);
net = 0;
mask = 0;
}
handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
if( handle == NULL ) {
fprintf(stderr, "could not open device %s : %s \n", dev, errbuf);
return 2;
}
if( pcap_compile(handle, &fp, filter_exp, 0, net) == -1 ) {
fprintf(stderr, "could not parse filter %s : %s \n", filter_exp, pcap_geterr(handle));
return 2;
}
if( pcap_setfilter(handle, &fp) == -1 ) {
fprintf(stderr, "could not install filter %s : %s \n", filter_exp, pcap_geterr(handle));
return 2;
}
int result = 0;
result = pcap_loop(handle, 0, got_packet, NULL);
if( result != 0 ) {
fprintf(stderr,"ERROR : pcap_loop() end with error !!! \n");
} else {
fprintf(stdout,"INFO : pcap_loop() end without error \n");
}
pcap_close(handle);
return 0;
} // end of main() .
void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char* packet)
{
#define SIZE_ETHERNET 14
const struct sniff_ethernet *ethernet;
const struct sniff_ip *ip;
const struct sniff_tcp *tcp;
const char *payload;
u_int size_ip;
u_int size_tcp;
ethernet = (struct sniff_ethernet*)(packet);
ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
size_ip = IP_HL(ip) * 4;
if( size_ip < 20 ) {
fprintf(stderr, " * Invalid IP Header Length %u bytes \n", size_ip);
}
tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
size_tcp = TH_OFF(tcp) * 4;
if( size_tcp < 20 ) {
fprintf(stderr, " * Invalid TCP Header Length %u bytes \n", size_tcp);
}
payload = (u_char*)(packet + SIZE_ETHERNET + size_ip + size_tcp);
unsigned short int payload_len = 0;
payload_len = ntohs(ip->ip_len) - size_ip - size_tcp;
// printf("DATA: payload_len %u \n", payload_len);
//
// printf("Jacked a packet with Length of [%d] \n", header->len);
//
//
// printf("DATA: dest MAC : %02x:%02x:%02x:%02x:%02x:%02x\n",
// ethernet->ether_dhost[0],
// ethernet->ether_dhost[1],
// ethernet->ether_dhost[2],
// ethernet->ether_dhost[3],
// ethernet->ether_dhost[4],
// ethernet->ether_dhost[5]
// );
//
//
//
// printf("DATA: src MAC : %02x:%02x:%02x:%02x:%02x:%02x\n",
// ethernet->ether_shost[0],
// ethernet->ether_shost[1],
// ethernet->ether_shost[2],
// ethernet->ether_shost[3],
// ethernet->ether_shost[4],
// ethernet->ether_shost[5]
// );
// IP
char *IPbuffer, *IPbuffer2;
char IPbuffer_str[16];
char IPbuffer2_str[16];
IPbuffer = inet_ntoa(ip->ip_src);
strcpy(IPbuffer_str, IPbuffer);
IPbuffer2 = inet_ntoa(ip->ip_dst);
strcpy(IPbuffer2_str, IPbuffer2);
// printf("DATA: IP src : %s \n", IPbuffer_str);
// printf("DATA: IP dst : %s \n", IPbuffer2_str);
// port
unsigned short tcp_src_port = 0;
unsigned short tcp_dst_port = 0;
tcp_src_port = ntohs(tcp->th_sport);
tcp_dst_port = ntohs(tcp->th_dport);
// printf("DATA : src Port %u \n", tcp_src_port);
// printf("DATA : dst Port %u \n", tcp_dst_port);
// domain
u_char *domain = NULL;
u_char *domain_end = NULL;
u_char domain_str[256] = { 0x00};
int domain_len = 0;
domain = strstr(payload, "Host: ");
if( domain != NULL ) {
domain_end = strstr(domain, "\x0d\x0a");
if( domain_end != NULL ) {
domain_len = domain_end - domain - 6;
strncpy(domain_str, domain + 6, domain_len );
// printf("INFO: Domain : %s \n", domain_str);
} else {
// printf("INFO: Host string not found \n");
}
}
// new -------------------------------------
// i know struct declare at the outside . ( temp )
struct check_domain_struct {
char domain[256];
};
// reset method 1 ( if i were have not DB )
//struct chk_domain_struct chk_domain_str[100] = { 0x00 };
//
//char *chk_domain_ptr[100] = { NULL };
//char *chk_strcpy[100] = { NULL };
//
//for(int i = 0; i < 100; i++) {
// chk_domain_ptr[i] = malloc(256);
// if( chk_domain_ptr[i] == NULL ) {
// fprintf(stderr, "ERROR: malloc() fail !! \n");
// }
//} // end for loop
//
//
//// strcpy & check
//strcpy(chk_domain_ptr[0], "naver.com");
//if( strlen(chk_domain_ptr[0]) == 0 )
// fprintf(stderr, "chk_domain_ptr[0] is NULL !! \n");
//strcpy(chk_domain_ptr[1], "kakao.com");
//if( strlen(chk_domain_ptr[1]) == 0 )
// fprintf(stderr, "chk_domain_ptr[1] is NULL !! \n");
//strcpy(chk_domain_ptr[2], "mail.naver.com");
//if( strlen(chk_domain_ptr[2]) == 0 )
// fprintf(stderr, "chk_domain_ptr[2] is NULL !! \n");
//// printf("%s \n", chk_domain_ptr[0]);
// reset method 2 ( declare & reset at the same time )
//struct chk_domain_struct chk_domain_str[100];
//for( int j = 0 ; j < 100 ; j++ ) {
// strcpy(chk_domain_str[j].domain, "");
//}
//// method 2 strcpy & check .
//strcpy(chk_domain_str[0], "naver.com");
//if( strlen(chk_domain_str[0]) == 0 )
// fprintf(stderr, "chk_domain_str[0] is NULL !! \n");
//strcpy(chk_domain_str[1], "kakao.com");
//if( strlen(chk_domain_str[1]) == 0 )
// fprintf(stderr, "chk_domain_str[1] is NULL !! \n");
//strcpy(chk_domain_str[2], "mail.naver.com");
//if( strlen(chk_domain_str[2]) == 0 )
// fprintf(stderr, "chk_domain_str[2] is NULL !! \n");
//// printf("%s \n", chk_domain_str[0]);
// reset method 3 ( use this )
int check_domain_str_count = 10000;
struct check_domain_struct *check_domain_str = NULL;
// malloc
check_domain_str = malloc ( sizeof(struct check_domain_struct) *
check_domain_str_count
);
if( check_domain_str == NULL ) {
fprintf(stderr, "ERROR: malloc fail !!! (line=%d) \n", __LINE__);
} else {
// fprintf(stdout,"INFO: malloc ok (line=%d) \n", __LINE__);
}
// reset 0
memset( check_domain_str, 0x00, sizeof( struct check_domain_struct ) *
check_domain_str_count
);
// method 3 strcpy & check .
strcpy(check_domain_str[0].domain, "naver.com");
if( strlen(check_domain_str[0].domain) == 0 )
fprintf(stderr, "check_domain_str[0] is NULL !! \n");
strcpy(check_domain_str[1].domain, "kakao.com");
if( strlen(check_domain_str[1].domain) == 0 )
fprintf(stderr, "check_domain_str[1] is NULL !! \n");
strcpy(check_domain_str[2].domain, "mail.naver.com");
if( strlen(check_domain_str[2].domain) == 0 )
fprintf(stderr, "check_domain_str[2] is NULL !! \n");
// printf("%s \n", check_domain_str[0]);
if( domain_len ) {
int cmp_ret = 1; // for compare result
// start for loop 1 .
for(int i = 0; i < 100; i++ ) {
// reset method 2
// cmp_ret = strcmp(check_domain_ptr[i], domain_str);
// if you knew str_len, you choice method like this
int str1_len = strlen ( check_domain_str[i].domain );
int str2_len = strlen ( domain_str );
if( str1_len != str2_len ) {
continue; // move to next array !
}
cmp_ret = strcmp(check_domain_str[i].domain, domain_str);
printf("DEBUG: domain name check result : %d \n", cmp_ret);
if( cmp_ret == 0 )
break; // stop for loop 1 .
// break if meet NULL data in array .
if( strlen( check_domain_str[i].domain) == 0 ) {
break; // stop for loop 1.
}
} // end for loop 1 .
printf("DATA: IP src : %s \n", IPbuffer_str);
printf("DATA: IP dst : %s \n", IPbuffer2_str);
printf("DATA : src Port %u \n", tcp_src_port);
printf("DATA : dst Port %u \n", tcp_dst_port);
printf("INFO: Domain : %s . \n", domain_str);
if( cmp_ret == 0 ) {
printf("DEBUG: main blocked . \n");
// sendraw(); // here is block packet function location later
} else {
printf("DEBUG: domain allowed . \n");
} // end if emp_ret .
if( check_domain_str != NULL ) {
free(check_domain_str);
check_domain_str = NULL;
} else {
fprintf(stderr, "CRIT: check_domain_str was already free status !! (line=%d) \n", __LINE__);
} // end check_domain_str
} // end if domain_len
// printf("\n");
} // end of got_packet()
코드 분석
domain 구조체 선언
struct check_domain_struct {
char domain[256];
};
domain 주소를 담을 구조체를 선언했다.
다른 방법들이 있지만 구조체로 하는 것부터 해보자.
1. reset을 하는 첫번째 방법
- 안 좋은 방법 이지만 DB가 없을 때 를 가정한 하드 코딩 방법이다.
1-1. 선언 부분
// reset method 1 ( if i were have not DB )
struct check_domain_struct check_domain_str[100] = { 0x00 };
char *chk_domain_ptr[100] = { NULL };
for(int i = 0; i < 100; i++) {
chk_domain_ptr[i] = malloc(256);
if( chk_domain_ptr[i] == NULL ) {
fprintf(stderr, "ERROR: malloc() fail !! \n");
}
} // end for loop
- 먼저 check_domain_struct 구조체를 사용해서 배열 100개를 만들고 그 자리를 모두 힙 영역에 malloc으로 메모리를 할당 시켜주었다.
- malloc을 사용할 때 항상 fail check를 할 것 !
// strcpy & check
strcpy(chk_domain_ptr[0], "naver.com");
if( strlen(chk_domain_ptr[0]) == 0 )
fprintf(stderr, "chk_domain_ptr[0] is NULL !! \n");
strcpy(chk_domain_ptr[1], "kakao.com");
if( strlen(chk_domain_ptr[1]) == 0 )
fprintf(stderr, "chk_domain_ptr[1] is NULL !! \n");
strcpy(chk_domain_ptr[2], "mail.naver.com");
if( strlen(chk_domain_ptr[2]) == 0 )
fprintf(stderr, "chk_domain_ptr[2] is NULL !! \n");
// printf("%s \n", chk_domain_ptr[0]);
- 하드코딩으로 도메인 주소를 담고 간단하게 길이로 check 해서 strcpy가 잘 되었는지 확인했다.
2. reset을 하는 2번째 방법
// reset method 2 ( declare & reset at the same time )
struct chk_domain_struct chk_domain_str[100];
for( int j = 0 ; j < 100 ; j++ ) {
strcpy(chk_domain_str[j].domain, "");
}
// method 2 strcpy & check .
strcpy(chk_domain_str[0], "naver.com");
if( strlen(chk_domain_str[0]) == 0 )
fprintf(stderr, "chk_domain_str[0] is NULL !! \n");
strcpy(chk_domain_str[1], "kakao.com");
if( strlen(chk_domain_str[1]) == 0 )
fprintf(stderr, "chk_domain_str[1] is NULL !! \n");
strcpy(chk_domain_str[2], "mail.naver.com");
if( strlen(chk_domain_str[2]) == 0 )
fprintf(stderr, "chk_domain_str[2] is NULL !! \n");
// printf("%s \n", chk_domain_str[0]);
- 간단하게 선언과 동시에 초기화를 해주고 사용한 방법이지만, 첫번째 방법과 두번째 방법 다 권유하지 않는다.
3. reset을 하는 3번째 방법 ( 이 방법 사용 )
3-1. 선언과 malloc()
// reset method 3 ( use this )
int check_domain_str_count = 10000;
struct check_domain_struct *check_domain_str = NULL;
// malloc
check_domain_str = malloc ( sizeof(struct check_domain_struct) *
check_domain_str_count
);
if( check_domain_str == NULL ) {
fprintf(stderr, "ERROR: malloc fail !!! (line=%d) \n", __LINE__);
} else {
// fprintf(stdout,"INFO: malloc ok (line=%d) \n", __LINE__);
}
- malloc() check는 필수이지만 성공했을 때를 굳이 check 할 필요 없어서 주석처리했다.
// reset 0
memset( check_domain_str, 0x00, sizeof( struct check_domain_struct ) *
check_domain_str_count
);
- 0으로 초기화 시켜줘야할 때 memset() 을 사용했다.
// method 3 strcpy & check .
strcpy(check_domain_str[0].domain, "naver.com");
if( strlen(check_domain_str[0].domain) == 0 )
fprintf(stderr, "check_domain_str[0] is NULL !! \n");
strcpy(check_domain_str[1].domain, "kakao.com");
if( strlen(check_domain_str[1].domain) == 0 )
fprintf(stderr, "check_domain_str[1] is NULL !! \n");
strcpy(check_domain_str[2].domain, "mail.naver.com");
if( strlen(check_domain_str[2].domain) == 0 )
fprintf(stderr, "check_domain_str[2] is NULL !! \n");
// printf("%s \n", check_domain_str[0]);
- strcpy() 와 check는 비슷하나, .domain으로써 구조체의 멤버 를 사용한다는 것 !! 잊지말자.
3-2. domain_len 의 값이 존재할 때 ( domain 값이 잡혔을 때 )
if( domain_len ) {
int cmp_ret = 1; // for compare result
// start for loop 1 .
for(int i = 0; i < 100; i++ ) {
// reset method 2
// cmp_ret = strcmp(check_domain_ptr[i], domain_str);
// if you knew str_len, you choice method like this
int str1_len = strlen ( check_domain_str[i].domain );
int str2_len = strlen ( domain_str );
if( str1_len != str2_len ) {
continue; // move to next array !
}
cmp_ret = strcmp(check_domain_str[i].domain, domain_str);
printf("DEBUG: domain name check result : %d \n", cmp_ret);
if( cmp_ret == 0 )
break; // stop for loop 1 .
// break if meet NULL data in array .
if( strlen( check_domain_str[i].domain) == 0 ) {
break; // stop for loop 1.
}
} // end for loop 1 .
printf("DATA: IP src : %s \n", IPbuffer_str);
printf("DATA: IP dst : %s \n", IPbuffer2_str);
printf("DATA : src Port %u \n", tcp_src_port);
printf("DATA : dst Port %u \n", tcp_dst_port);
printf("INFO: Domain : %s . \n", domain_str);
if( cmp_ret == 0 ) {
printf("DEBUG: main blocked . \n");
// sendraw(); // here is block packet function location later
} else {
printf("DEBUG: domain allowed . \n");
} // end if emp_ret .
if( check_domain_str != NULL ) {
free(check_domain_str);
check_domain_str = NULL;
} else {
fprintf(stderr, "CRIT: check_domain_str was already free status !! (line=%d) \n", __LINE__);
} // end check_domain_str
} // end if domain_len
- 이번 부분이 어려운 부분이 없고 설명할 것들을 주석으로 넣어놔서 피드백으로 넘어가서 컴파일 중 주의해야했던 부분들을 적어놓자.
피드백 & Tips
- gdb로 디버거 활용하는 방법을 알아내었고 수시로 잘 쓰도록 하자
간단한 사용법이다.
- gcc로 컴파일 할 때 -g 옵션을 넣어서 gdb를 사용할 수 있게 한다.
- 예시: gdb pcap-001 으로 실행한다
- Run 으로 실행하고 다른 terminal에서 curl로 패킷전송을 시작한다.
- SegmentFault Core Dump 오류가 떴을 때 BackTracer 명령어로 쌓인 스택 을 찾아간다.
- 이때 보이는 라인의 문법에 문제 가 있는 것이니 수정한다.
- 코어덤프가 아닌 다른 오류가 생긴다면 Next 명령어로 다음을 실행해보며 찾아가면 된다.
- 오류가 생길 것 같이 예측이 되는 부분은 printf() 를 사용할 때 (line=%d) 을 사용하고, LINE 이라는 인수를 주면 해당 라인의 번호가 같이 출력된다 !
현재까지의 진행상황 중 느낀점
몇 가지 궁금증이 생겼다.
먼저,
1. 이 프로젝트 의 전체 흐름을 보아 장단점 은 무엇이라 생각하는가?
-
장점
도메인들을 추가하거나 삭제하여 유해사이트를 차단할 수 있다고 생각한다.
현재는 DB와 연결되어있지 않아 하드코딩으로 추가하고 삭제하고 있지만,
Web과 DB를 활용한다면 편리하게 사이트를 차단하고 해제할 수 있다고 생각한다.
또한, 유해사이트를 차단하는 전체 흐름을 이해하고 배울 수 있다는 점이 가장 크다고 생각한다. -
단점
역시나 http에서만 작동한다는게 가장 큰 문제점이다.
언젠가는 꼭 https 에서도 작동하는 것들을 배우고 싶다.
2. https가 아닌 http에서의 보안 기능을 왜 배운다고 생각하는가?
현재 거의 사용하지 않는 http지만 유해사이트의 차단 흐름이 어떤 식으로 흘러가는지 파악할 수 있었고,
이 프로젝트 를 진행하면서 능력이 쌓이고, 오류들을 해결하는 과정들이 경험이 되었다.
그로인해 관심과 실력이 늘어날 수 있다면 http로 시작하는 지금의 과정도 중요하다고 생각한다.
기본이 없으면 응용은 배울 수 없는 법이니, 기본부터 탄탄하게 쌓아가자.
댓글 남기기