반응형

2022.03.13 - [C언어/C언어 개발] - C언어 DNS쿼리 프로그램 만들기 gethostbyname() on linux

 

얼마 전에 만들었던 DNS쿼리 프로그램인데, 여기서 몇가지 기능을 추가하여 업그레이드 하였다.

  1. 파일의 경로와 필요한 몇가지 데이터를 argument로 받는 기능 추가
  2. 프로그램의 총 실행 시간을 알려주는 기능 추가
  3. gethostbyname() 함수의 응답이 일정 시간(사용자가 입력한 값)이 지나면 다음 도메인으로 넘어가는 timeout 기능 추가
  4. 전체 파일을 사용자가 입력한 값으로 나누는 기능 추가 (총 100줄이라면 20줄씩 끊을 수 있는 기능) 

 

 

1. 파일 경로와 몇가지 데이터를 argument로 받는 기능을 추가한 이유는 코드 상에 파일 경로가 적혀 있으면 파일 경로를 수정하고 싶을 때 코드를 수정하고 다시 빌드해야하는 번거로움이 있어, argument를 통해 받는 것으로 수정하였다.

argument로 받는 데이터는 다음과 같다.

argument 목록
argv[0]은 당연하게도 자동으로 자기 자신의 경로가 들어간다.

argv[1] 읽어들일 파일의 경로
argv[2] 가공하고 저장할 파일의 경로
argv[3] 몇줄씩 자를 것인지 (총 100줄이라면, 20을 입력하면 20줄씩 자른다.)
argv[4] 타임아웃 시간 (gethostbyname의 리턴값이 입력한 초 동안 오지 않으면 다음 도메인으로 점프)

 

 

 

2. 총 실행 시간을 알려주는 기능을 추가한 이유는 약 n개의 도메인을 대입 했을 때 걸리는 시간을 알고 싶어서 추가 하였다. 속도는 빠르면 빠를수록 좋으니까 최적화를 하는 작업에서 시간 비교를 많이 하였다.

 

 

 

3. timeout기능을 추가한 이유는 2번의 기능을 추가하고 난 뒤에, 당연하겠지만 도메인이 많아지면 많아질 수록 시간이 오래 걸렸다. 도메인마다 평균 응답 속도가 1초라고 정의 했을 때, 만약 파일의 있는 도메인의 양이 약 5천만개가 된다면 이 프로그램이 끝나는 시간은....  19달 즉, 1년 7개월이 걸린다 돌려놓고 군대 갔다와도 되겠다.

엄청나다.

그래서 생각해낸 방법이 filter방식인데, timeout을 구현하여 처음에는 0.1초로 걸어놓고 응답 받는 것은 받는대로 저장하고 응답 받지 못한 도메인은 따로 저장하여 다음에는 0.2초로 돌리고 여기서 걸러진 것도 0.3초로 돌려보고 이런식이다.  timeout은 구현 완료 하였으니

 

0.1, 0.2 filter를 자동으로 실행하는 기능은 3.0버전에 구현해보려고 한다.

 

 

 

4. 파일의 총 문자열을 사용자가 입력한 값으로 나누는 기능을 추가한 이유는 사실 3번으로도 걸리는 시간을 충분히 단축 할 수 있을 거라고 생각 했지만, 일단 프로그램을 여러번 돌려야 한다는 단점이 있었기 때문에 한번에 최대한 많은 도메인을 처리해보자라고 생각 해서 스레드 방식을 고안 했었다. 그래서 파일의 도메인들을 사용자가 원하는 길이만큼 잘라서 10줄씩 자른다면 1스레드당 10개씩 할당하여 처리하는 방식을 생각 했었는데

현재는 자르는 것만 구현 완료 하였으니

 

스레드 기능은 3.0버전에 구현해보려고 한다.

 

 

 

정리) 3.0 버전에는 filter자동 실행 기능과 스레드 처리 기능을 구현해보겠다.

 

아래 코드는 전과 비슷하여 세세한 설명은 주석에 달아 놓겠다.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>

static jmp_buf env;
struct hostent *host;

void sig_handler () {
    longjmp (env, 1); //알람이 울리면 setjmp가 호출된 곳으로 점프하면서 setjmp가 1을 반환 (2)
}

int dnslookup (const char* domain, FILE **fp, long tout) {
    int status = 0;
    signal (SIGALRM, sig_handler); //알람이 울리면 sig_handler를 호출한다. (1)

    if (0 == setjmp(env)) {  //1과 0으로 시간을 지났는지 판단하고 있다가 응답이 오면 해당 도메인에 대한 IP입력 (3) END
        alarm (tout);
        host = gethostbyname (domain);
        if (host != NULL){
            status = 1;  //시간이 지나면 status에 0은 그대로 유지 되어 아래 if구문을 타게되고 (4)
            fprintf(*fp, "%s / %s\n", domain, inet_ntoa(*(struct in_addr *)host->h_addr_list[0]));
        }
        signal (SIGALRM, SIG_DFL);
        alarm (0);
    } else { //혹시나 예외처리
        host = NULL;
        fprintf(*fp, "%s / %s\n",domain, "FAIL");
    }

    if(status == 0){
        fprintf(*fp, "%s / %s\n",domain, "FAIL"); //FAIL입력 (5)
    }

    return status;
}

int main(int argc, char *argv[]){
    #define IPATH argv[1]
    #define OPATH argv[2]

    if (argc < 5) { //argv의 개수가 맞지 필요한 만큼 없으면 오류가 뜨기 때문에 예외 처리
        printf("옵션 개수를 정확하게 맞춰주세요.\n");
        exit(0);
    }

    long argv3ToNum;
    long argv4ToNum;

    argv3ToNum = strtol(argv[3], NULL, 10); //이 부분이 중요한데, argv로 받으면 문자열로 받게 된다.
    argv4ToNum = strtol(argv[4], NULL, 10); //나는 길이를 입력 받기 때문에 정수와 비교해야 하는데,
                                                                    //문자를 long형태로 바꿔주는 구문이다.

    struct timeval  tv; //시간 구조체
    double begin, end;
    gettimeofday(&tv, NULL);
    begin = (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000 ; //프로그램 시작시간 입력


    char *ListBuffer;
    int ListSize;

    FILE *InputFile = fopen(IPATH, "r");
    FILE *OutputFile = fopen(OPATH, "w");

    fseek(InputFile, 0, SEEK_END);
    ListSize = ftell(InputFile); //길이를 재고

    ListBuffer = malloc(ListSize); //잰 길이만큼 동적할당
    memset(ListBuffer, 0, ListSize); //할당한 메모리 초기화

    fseek(InputFile, 0, SEEK_SET);
    fread(ListBuffer, ListSize, 1, InputFile); //읽어와서 동적할당한 배열에 넣기

    int count = 0;
    char dest[ListSize];
    int before_i = 0;
    int first_line = 0;

    char *str1;
    char *temp1;

    char *str2;
    char *temp2;

    char *str3;
    char *temp3;

    for (int i = 0; i <= ListSize; i++) { //여기부터는 직접 배열의 index를 넣어보면서 코드를 짠 것이라 설명하기 어렵다.
        if (ListBuffer[i] == '\n') {
            count++;
            if(count == argv3ToNum) { //첫번째 예외 처리
                if(first_line == 0){
                    first_line ++;
                    strncpy(dest, ListBuffer, i);
                    dest[i] = '\0';
                    str1 = NULL;
                    temp1 = strtok_r(dest, "\n", &str1);
                    while (temp1 != NULL){
                        dnslookup(temp1, &OutputFile, argv4ToNum);
                        temp1 = strtok_r(NULL, "\n", &str1);
                    }
                    before_i = i;
                    count = 0;
                }
                else {
                    strncpy(dest, ListBuffer + before_i + 1, (i + 1) - (before_i + 2));
                    dest[(i + 1) - (before_i + 2)] = '\0';
                    str2 = NULL;
                    temp2 = strtok_r(dest, "\n", &str2);
                    while (temp2 != NULL){
                        dnslookup(temp2, &OutputFile, argv4ToNum);
                        temp2 = strtok_r(NULL, "\n", &str2);
                    }
                    before_i = i;
                    count = 0;
                }
            }
        }
        else if(ListBuffer[i] == '\0'){ //마지막 예외 처리
            strncpy(dest, ListBuffer + before_i + 1, i - before_i + 1);
            dest[i - before_i + 1] = '\0';
            str3 = NULL;
            temp3 = strtok_r(dest, "\n", &str3);
            while (temp3 != NULL){
                dnslookup(temp3, &OutputFile, argv4ToNum);
                temp3 = strtok_r(NULL, "\n", &str3);
            }
        }
    }

    fclose(InputFile); //파일 포인터 닫기
    free(ListBuffer); // 동적 메모리 해제
    fclose(OutputFile); //파일 포인터 닫기


    ListBuffer = NULL;


    gettimeofday(&tv, NULL);
    end = (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000 ; //프로그램 종료시간 대입


    printf("Execution time %f\n", (end - begin) / 1000); //(시작시간 - 종료시간) 출력
}

궁금한 사항이 있으면 댓글로 질문 달아주기 바랍니다.

 

 

 

이건 깃허브

https://github.com/Wonlf/AutoDNS-Query

 

GitHub - Wonlf/AutoDNS-Query: automatic DNS query program running on Linux

automatic DNS query program running on Linux. Contribute to Wonlf/AutoDNS-Query development by creating an account on GitHub.

github.com

 

반응형