반응형

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

※ver2.0 나왔습니다 위 링크를 참고해주세요.

 

먼저, 파일의 전체적인 구조는 input.txt에 저장되어 있는 텍스트들을 읽어와서 한줄씩 DNS쿼리를 날려 도메인에 해당하는 아이피를 받아오고 양식에 맞춰 output.txt에 저장하는 로직으로 구성된 프로그램이다.

 

이 프로그램에서 가장 중요한 함수는 gethostbyname함수인데, 이 녀석이 내가 원하는 기능을 가지고 있다.

gethostbyname()은 hostent 타입의 구조체를 반환하는 함수이며, hostent, gethostbyname모두 netdb헤더 파일에 있다.

 

gethostbyname함수 파라미터로는 호스트 이름 또는 IP주소가 필요하고, 함수의 리턴값으로는 hostent에 해당하는 형식으로 리턴된다.

함수 에러시 NULL을 리턴한다.

 

hostent에 해당하는 구조체 형식은 이렇게 되어 있다.

struct hostent{
  char *h_name;                 /* Official name of host.  */
  char **h_aliases;             /* Alias list.  */
  int h_addrtype;               /* Host address type.  */
  int h_length;                 /* Length of address.  */
  char **h_addr_list;           /* List of addresses from name server.  */
  #define h_addr  h_addr_list[0]  /* Address, for backward compatibility.  */
};

 

먼저, gethostbyname함수 리턴값을 받을 변수와 파일을 읽어 왔을 때 저장해놓을 버퍼(변수), 그리고 파일의 전체 크기를 저장할 변수 총 3개를 선언해준다.

struct hostent *host;
char *ListBuffer;
int ListSize;

 

그리고 읽을 파일과 쓰기할 파일 포인터도 선언해준다.

#define IPATH "input.txt"
#define OPATH "output.txt"

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

 

파일의 형식은 이렇게 생겼다.

www.google.com
www.naver.com
kbphonemall.com

 

fseek함수를 이용하여 SEEK_END파라미터로 파일 포인터를 파일의 맨 끝으로 이동시켜, 처음부터 끝까지 파일의 전체 크기를 잰다.

fseek(InputFile, 0, SEEK_END);
ListSize = ftell(InputFile);

 

잰 길이만큼 메모리에 동적할당 해주고, memset함수를 이용하여 전부 0으로 초기화시킨다. 

ListBuffer = malloc(ListSize);
memset(ListBuffer, 0, ListSize);

 

 

fseek함수를 이용하여 SEEK_SET파라미터로 파일 포인터를 파일의 맨 앞으로 이동시켜, fread함수로 읽어와 할당해놓은 메모리에 넣는다.

fseek(InputFile, 0, SEEK_SET);
fread(ListBuffer, ListSize, 1, InputFile);

 

리눅스에서는 LF형식으로 줄바꿈을 하기 때문에, \n을 키로 사용하여 도메인별로 구분한다. 그러기 위해서 strtok함수를 사용하였다. 사용하게 되면 맨 첫번째 나오는 \n로 자르고 그 뒤에 문자열은 남아있게 된다. 그래서 while문으로 전체 문자열을 끝까지 돌게 구현하였다. 필자는 컴파일은 리눅스에서 하고, 코딩은 윈도우에서 하다보니 이것 때문에 오류가 났었다. 더 궁금하면 링크로...

 

2022.03.13 - [컴퓨터 잡] - CRLF / LF / CR 차이점

 

CRLF / LF / CR 차이점

CR : Carriage Return은 \r에 해당하고 LF : Line Feed은 \n에 해당한다. 그리고 CRLF는 둘 다 쓰겠다는 의미이다. 이 두개가 사용되는 이유는 줄바꿈 때문인데, 옛날에 타자기를 사용할 때도 썼던 방식이라

wonlf.tistory.com

 

그리고 자른 문자열을 gethostbyname함수에 넣고 돌려서 반환값을 받으면 fprint로 파일에 쓴다.

여기서 inet_ntoa함수를 사용 했는데, 이것은 주소 표현을 위해 사용된 in_addr이 unsigned_long형식이기 때문에 친숙한 10진수로 바꿔주는 역할을 하는 함수가 inet_ntoa이다. 그 뒤 코드는 다 알거라고 생각한다.

 

여기서 중요한점 예외처리는 선택이 아닌 필수 입니다. host == NULL이거 안했다고 반환값이 NULL이면 값이 들어가지 않기 때문에 Segment fault 떴는데 이상한 곳에서 원인 찾고 있었음 

char *str1 = NULL;
char *temp1 = strtok_r(ListBuffer, "\n", &str1);


while (temp1 != NULL){
    host = gethostbyname(temp1);
    if (host == NULL){
        fprintf(OutputFile, "%s / %s\n",temp1, "FAIL");
    }
    else{
        fprintf(OutputFile, "%s / %s\n", temp1, inet_ntoa(*(struct in_addr *)host->h_addr_list[0]));
    }
    temp1 = strtok_r(NULL, "\n", &str1);
}

 

나는 우분투에서 컴파일 하다보니 gcc를 사용해서 개발하였는데, gcc main.c -o main.out치고 ./main.out 치는게 귀찮아서 쉘 파일로 만들었다. 그냥 저 명령어 두개를 텍스트에디터에서 쓴 뒤에 .sh파일로 저장하면 된다.

 

그렇게 해서 쉘 파일 실행시켜주면,

이렇게 된다.

 

전체 코드이다.

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

#define IPATH "input.txt"
#define OPATH "output.txt"


int main(int argc, char *argv[]){

    struct hostent *host;
    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); //읽어와서 동적할당한 배열에 넣기

    char *str1 = NULL;
    char *temp1 = strtok_r(ListBuffer, "\n", &str1);

    
    while (temp1 != NULL){
        host = gethostbyname(temp1);
        if (host == NULL){
            fprintf(OutputFile, "%s / %s\n",temp1, "FAIL");
        }
        else{
            fprintf(OutputFile, "%s / %s\n", temp1, inet_ntoa(*(struct in_addr *)host->h_addr_list[0]));
        }
        temp1 = strtok_r(NULL, "\n", &str1);
    }

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


    ListBuffer = NULL;
}
//주의 텍스트파일 만들 때, 윈도우에서 만들면 파일 형식이 "CRLF"인데 리눅스에서 파일을 만들면 "LF"형식이다
// CRLF형식이면 프로그램 오류 뜨니까 리눅스에서 파일 만들기
// 첫번째 방법 배열에 쌓아뒀다가 다 하고 마지막에 파일에 쓰기
// 두번째 방법 처리 할때마다 파일에 쓰기 근데 이게 중간에 취소 됐을때를 생각하면 이게 나음

 

 

 

 

이건 깃허브

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

 

반응형