[Project] 프로젝트 C언어를 사용하여 DB와 연동

Date:     Updated:

카테고리:

태그:

전체 코드

코드
#include <stdio.h>
#include <pcap.h>
#include <string.h>
#include <stdlib.h>
#include <mysql.h>
#include <time.h>
 
 
// MySQL
struct connection_details {
 
    char *server;
    char *user;
    char *password;
    char *database;
 
};

// function()
MYSQL_RES* mysql_perform_query(MYSQL *connection, char *sql_query);
 
// PCAP
#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;

	// 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);

	// 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);

	// 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 -------------------------------------
	struct check_domain_struct {
		char domain[256];
	};

	// 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");

	if( domain_len ) {
		int cmp_ret = 1; // for compare result

		// start for loop 1 .
		for(int i = 0; i < 100; i++ ) {

		// 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);
		
		
		// new -------------------------
		
		// DB
		MYSQL_RES *res;
		MYSQL_ROW row;

		MYSQL* conn = mysql_init(NULL);
		if (conn == NULL) {
			printf("MySQL initialization failed");
			return;
		}
		
		struct connection_details mysqlD;
		mysqlD.server = "localhost";
		mysqlD.user = "root";
		mysqlD.password = "1234";
		mysqlD.database = "project";

		if (mysql_real_connect(conn, mysqlD.server, mysqlD.user, mysqlD.password, mysqlD.database, 0, NULL, 0) == NULL) {
			printf("Unable to connect with MySQL server\n");
			mysql_close(conn);
			return;
		}

		char query[1024] = { 0x00};
		
		// for time check
		time_t t1;
        time(&t1);
        
        char* time_buf = ctime(&t1);
        time_buf[strlen(time_buf)-1] = '\0';
        printf("ctime의 결과 : %s\n", time_buf);

		//dd
		sprintf(query, "INSERT INTO Recent_list VALUES('%s', '%s', '%d', '%s')", domain_str, IPbuffer2_str, tcp_dst_port, time_buf);
		
		// mysql_perform_query(conn, query);
		if( mysql_query(conn, query) ) 
			printf("mysql_query Sucess \n");

		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

		res = mysql_perform_query(conn, "SELECT * FROM Recent_list");

		printf("\n");
		printf("Mysql contents in mysql Recent_list \n");
		while( (row = mysql_fetch_row(res) ) != NULL){
			printf("Domain: %20s | ", row[0]);
			printf(" IP: %15s | ", row[1]);
			printf(" Port: %7s | ", row[2]);
			printf(" Time: %s . \n", row[3]);
		}
		printf("\n");
		mysql_free_result(res);
		mysql_close(conn);

		} // end if domain_len
} // end of got_packet()

// query function() for print of DB contents
MYSQL_RES* mysql_perform_query(MYSQL *connection, char *sql_query) {
 
    if(mysql_query(connection, sql_query)) {
        printf("MYSQL query error : %s\n", mysql_error(connection));
        exit(1);
    }
    return mysql_use_result(connection);
}









분석

1. 선언과 초기화

    MYSQL* conn = mysql_init(NULL);
    if (conn == NULL) {
        printf("MySQL initialization failed");
        return;
    }
  • MYSQL* conn = mysql_init( NULL ) ;

이 부분은 MYSQL 구조체를 사용하여 conn 이란 변수를 초기화하는데,
mysql의 초기화 함수인 mysql_init() 을 사용했다.

1-1. mysql_init( )

MYSQL * mysql_init(MYSQL * mysql);


mysql - a pointer to MYSQL or NULL. 
In case of passing a NULL pointer mysql_init() will allocate memory 
and return a pointer to a MYSQL structure.
  • 위 문법 설명을 보면 MYSQL의 포인터를 가리키던지 아니면 NULL을 준다고 한다.
  • 그리고 NULL을 매개변수로 주었을 때 mysql_init() 은 메모리를 할당할 것이며, MYSQL 구조체를 가르키는 포인터를 반환 한다 !


Prepares and initializes a MYSQL structure to be used with mysql_real_connect().
If mysql_thread_init() was not called before, 
mysql_init() will also initialize the thread subsystem for the current thread
  • 설명부분을 보면 mysql_real_connect() 를 사용하여 MYSQL 구조체 를 준비하고 초기화할 때,
    만약 mysql_thread_init() 사용하지 않았었다면,
    mysql_real_connect() 는 지금의 스레드를 위해 스레드 서브시스템으로 초기화 할 것이다. 라는 뜻이다.





2. 연결 및 데이터 추가

    char* server = "localhost";
    char* user = "root";
    char* password = "1234";
    char* database = "project";

    if (mysql_real_connect(conn, server, user, password, database, 0, NULL, 0) == NULL) {
			printf("Unable to connect with MySQL server\n");
			mysql_close(conn);
			return;
	}
  • mysql_real_connect() 함수를 사용하여, conn 이라는 MYSQL 구조체에 server 이름과, user의 이름, password,
    database의 이름, 포트번호, unix_socket인데 NULL로 주었고, client_flag의 값을 0으로 준 것이다.

2-1. mysql_real_connect()

MYSQL *
mysql_real_connect(MYSQL *mysql,
                   const char *host,
                   const char *user,
                   const char *passwd,
                   const char *db,
                   unsigned int port,
                   const char *unix_socket,
                   unsigned long client_flag)
  • 중요한 것들만 가져온다면, const char *db 는 적어놓은 DATABASE의 이름을 connection에서의 default Database 로 설정한다.
    ( 없는 DB이름을 적어도 오류가 나지 않으니 주의할 것 )

  • 설명이 굉장히 많으므로, 링크를 남기겠다.
  • https://dev.mysql.com/doc/c-api/8.0/en/mysql-real-connect.html

2-2. sprintf() , mysql_query()

char query[1024] = { 0x00};
    // query setting
    sprintf(query, "INSERT INTO Recent_list VALUES('%s', '%s', '%d', '%s')", domain_str, IPbuffer2_str, tcp_dst_port, time_buf);
    
    // mysql_perform_query(conn, query);
    if( mysql_query(conn, query) ) 
        printf("mysql_query Sucess \n");
  • 먼저 이번에 처음 배우게 된 sprintf() 함수는 printf() 와 기능은 비슷하지만, 거기에 문자열에 추가할 수 있는 용도로 쓸 수 있는 함수이다. ( 굉장히 유용하다 )

2-3. mysql_query()

int
mysql_query(MYSQL *mysql,
            const char *stmt_str)
  • stmt_str에 있는 DB 명령어 문자열들을 매개변수 mysql이 가리킨 MYSQL 구조체에서 실행한다.

성공시: 0
실패시: ERROR ( multiple )

3. DB안에 있는 내용들 불러오기

MYSQL_RES *res;
MYSQL_ROW row;

res = mysql_perform_query(conn, "SELECT * FROM Recent_list");

printf("Mysql contents in mysql Recent_list \n");
    while( (row = mysql_fetch_row(res) ) != NULL){
        printf("Domain: %20s | ", row[0]);
        printf(" IP: %15s | ", row[1]);
        printf(" Port: %7s | ", row[2]);
        printf(" Time: %s . \n", row[3]);
    }


// query function() for print of DB contents
MYSQL_RES* mysql_perform_query(MYSQL *connection, char *sql_query) {
 
    if(mysql_query(connection, sql_query)) {
        printf("MYSQL query error : %s\n", mysql_error(connection));
        exit(1);
    }
    return mysql_use_result(connection);
}

  • 자료형: MYSQL_RES : SELECT 등 결과를 리턴하는 query의 결과를 나타내는 자료형이다.
  • 자료형: MYSQL_ROW : MYSQL_RES에서 하나의 레코드씩 값을 얻어 올때 쓰이는 자료형이다.

3-1. mysql_use_result()

MYSQL_RES *
mysql_use_result(MYSQL *mysql)
  • 쿼리를 실행한 mysql의 결과를 자료형: MYSQL_RES 형으로 반환하는 함수이다.
    즉, return values 를 받기 위해서는 MYSQL_RES 형으로 선언한 변수가 필요하다.

3-2. mysql_perform_query()

res = mysql_perform_query(conn, "SELECT * FROM Recent_list");

MYSQL_RES* mysql_perform_query(MYSQL *connection, char *sql_query) {
 
    if(mysql_query(connection, sql_query)) {
        printf("MYSQL query error : %s\n", mysql_error(connection));
        exit(1);
    }
    return mysql_use_result(connection);
}
  • DB데이터를 불러 올 때 써야했는데, 일단 그 이유는 밑의 mysql_fetch_row() 함수를 설명할 때 얘기하도록 하자.
  • 먼저 mysql_query() 와 동일한 기능을 하지만, 여기선 mysql_use_result() 함수를 반환하고, 그 값을 받기 위해서 사용했다.

3-3. mysql_fetch_row()

MYSQL_ROW
mysql_fetch_row(MYSQL_RES *result)
  • MYSQL_RES result 안에 있는 값들을 한 줄씩 불러오기 위해서 사용하며,
    NULL 이 나왔을 경우 더 이상의 데이터는 없다는 것을 의미한다.

4. time() , ctime()

    // for time check
    time_t t1;
    time(&t1);
    
    char* time_buf = ctime(&t1);
    time_buf[strlen(time_buf)-1] = '\0';
    printf("ctime의 결과 : %s\n", time_buf);

4-1. time()

time_t time(time_t *pTime)
  • time() 은 1970년 1월 1일 0시(UTC)부터 현재까지 흐른 시간을 time_t 타입(초단위 정수)로 반환을 해준다.
  • 그래서 변환을 위한 ctime() 함수를 사용하는 것이다.

4-2. ctime()

char* ctime(const time_t* pTime);
  • 반환형 : Www Mmm dd hh:mm:ss yyyy
  • 나중에 이걸 사용하더라도 strstr() 를 사용하면 될 듯 하다.

5. mysql_free_result()

mysql_free_result(res);
mysql_close(conn);
  • mysql_query() 을 사용했다면, mysql_free_result() 를 반드시 마지막에 사용해서 메모리를 해제해줘야한다.

Project 카테고리 내 다른 글 보러가기

댓글 남기기