정글사관학교 개발일지/웹 서버

정글사관학교 52일차 TIL: tiny 서버 구현, string 관련 함수 정리

Woonys 2021. 12. 24. 01:12
반응형

1. tiny 서버 구현

Tiny main() 루틴

  • 타이니는 포트로부터 오는 반복적으로 연결 요청을 듣기 수행하는 서버.
  • Open_listenfd() 함수를 호출해 듣기 소켓을 오픈한 다음, 무한한 서버 루프를 실행 ⇒ 반복적으로 accept() 을 실행해 연결을 받고, 트랜잭션을 수행한 뒤, 연결을 닫아.

doit 함수

  • doit 함수는 HTTP 트랜잭션을 다룬다.
    1. 요청라인을 읽고 parsing. 이때, rio_readlineb 함수를 이용해 request line을 읽는다.
      • *rio_readinitb, rio_readlineb
        • 텍스트 라인 전체를 내부 읽기 버퍼에서 복사하는 rio_readlineb와 텍스트 라인과 바이너리 데이터 모두를 읽을 수 있는 rio_readnb
        • 파일 → 읽기 버퍼 → (읽기 버퍼를 읽고 그 데이터를) 메모리 버퍼로 복사 
          ssize_t rio_readlineb(rio_t* rp, void* usrbuf, size_t maxlen);
          ssize_t rio_readnb(rio_t* rp, void* usrbuf, size_t n);
        • #include "csapp.h" void rio_readinitb(rio_t* rp, int fd);
        • 💡 `rio_readinitb`: 읽고 싶은 파일 식별자 `fd` 와 읽기 버퍼 `rp` 를 연결한다.
      • RIO 버퍼를 통한 입력 함수
    • TINY는 온리 GET 메서드만 지원. 만약 클라이언트가 POST같은 다른 메서드로 요청하면 에러 메시지 반환. 그리고 해당 연결을 close하고 다음 connection request가 올 때까지 대기. 아니면 request header를 읽고 무시.
    1. 다음으로, URI를 filename과 빈 CGI argument string 으로 parse(동적 콘텐츠로 들어올 가능성이 있으니). 이후에는 플래그를 설정 → 플래그는 요청이 static인지 dynamic인지를 알려줌. 만약 파일이 서버 내 디스크에 없다면 에러 메시지를 클라이언트한테 보냄.
    2. request가 static이면: 파일이 regular 파일인지 & 우리가 읽기 권한이 있는지를 검증. 만약 가능하다면 ⇒ static content를 서빙.
    3. 💡 regular 파일: 리눅스/유닉스에서 어떤 형태의 데이터를 저장하는 가장 흔한 유형의 파일. 각종 텍스트 파일, 실행 파일, 이미지 파일 등 리눅스에서 사용하는 대부분 파일이 일반 파일에 해당.이외에 다른 어떤 파일이 있나? 디렉토리 파일(리눅스에서는 디렉토리도 파일로 생각), 블록 파일, 소켓 파일 등등
    4. 만약 요청이 동적 콘텐츠라면 ⇒ 파일이 실행가능한지를 먼저 검증한 다음, 가능하다면 동적 콘텐츠를 보낸다.

clienterror 함수

  • 클라이언트 에러를 체크하고 클라이언트에게 보고.
    • 상태 코드와 상태 메시지를 response line에 반환.

Read_requesthdrs 함수

  • TINY는 request header에 어떤 정보도 사용하지 X. 그냥 읽고 무시.

parse_uri 함수

  • TINY는 정적 콘텐츠에 대한 홈 디렉토리를 현재 디렉토리라고 간주 & 실행가능 파일(동적 콘텐츠)에 대한 홈 디렉토리는 ./cgi-bin 에 있다고 간주. URI에 cgi-bin 을 포함하면 그걸 무조건 동적 콘텐츠라고 간주. 디폴트 파일명은 ./home.html
  • parse_uri는 이 정책을 수행하는 함수. 이 함수는 URI를 파일명과 추가 CGI 인자로 쪼갠다.
  • 만약 요청이 static이면 ⇒ CGI argument string을 지운 다음 URI를 리눅스 pathname으로 바꾼다.
  • 만약 URI가 “/”로 끝나면 ⇒ 디폴트 파일명을 추가해준다.
  • 그렇지 않고 만약 동적 콘텐츠라면 ⇒ CGI argument를 추출한 다음 남은 URI 부분을 리눅스 파일명으로 변환한다.

serve_static 함수

  • TINY는 5가지 정적 콘텐츠 유형을 제공 → HTML, text, img(gif, png, jpeg)
  • serve_static: HTTP response를 보내줌 → body는 로컬 파일 콘텐츠를 담고 있다.

serve_dynamic 함수

  • TINY는 동적 콘텐츠를 제공하는데, 자식 프로세스를 포킹한 뒤, CGI 프로그램을 자식 프로세스 안에서 동작시켜서 제공.
  • serve_dynamic 함수는 response line을 클라이언트에게 보내면서 시작.
    • CGI 프로그램이 응답의 나머지를 보내줘야 함.
  • response 첫 파트를 보내고 나면, 새로운 자식 프로세스를 포크. 자식 프로세스는 QUERY_STRING 환경변수를 URI 요청으로부터의 CGI 인자로 초기화.
    • 실제 서버가 다른 CGI 환경 변수를 설정
  • 다음으로, child는 child 프로세스의 표준 출력을 연결 파일 디스크립터로 리다이렉트.

나중에 다시 정리할 때 참고하기 => DK's github

 

2. string 관련 함수 정리

sprintf() 함수 & printf()와의 차이

printf: 해당 값을 모니터에 출력

sprintf(): 가장 첫번째 인자에 뒤에 오는 값을 넣고 저장하는 방식. 새 변수에 값을 넣는다고 생각하면 됨.

strcpy 함수

char * strcpy ( char * destination, const char * source );

str+cpy: 문자열을 복사하는 기능을 가진 함수. 이 함수는 어떤 변수(메모리) 또는 문자열 상수에 저장되어 있는 문자열을 다른 변수(메모리)에 복사할 때 사용함. 이때, 복사할 원본 문자열에는 끝에 NULL 문자가 포함되어 있음. (***C언어에서는 문자열 끝에 반드시 NULL이 들어있어야 한다.)**

strcpy 함수는 위에서 보다시피 source의 길이를 제한하지 않는다. 따라서 아래 설명하는 buffer overflow가 발생할 수 있다. 그래서 주로 사용하는 함수는 strcpy_s()로, 이 친구는 복사해오는 문자열의 길이 한도를 애초에 인자로 받아오기 때문에 오버플로우를 발생시키지 않는다.

버퍼(Buffer)

버퍼란 운영체제에서 관리해주는 임시 공간을 뜻한다. 예전에 알고리즘 공부하면서 파이썬에서는 stdin.readline()으로 입력값을 받는 게 input()보다 빠르다는 걸 배운 적이 있다. 그 당시에 버퍼에 대해 잠깐 공부했는데, 이렇게 다시 만날 줄이야. 근데 그때 내가 공부한 건 되게 얄팍한 수준이었다.

C언어에서는 버퍼에 대해 이렇게 설명한다. 특정 크기의 메모리 공간이며 중간에 임시 저장하기 위한 용도로 사용한다. 여기서도 깊게 다루면 한도 끝도 없으니 여기까지만. 아래 버퍼 오버플로우만 간단하게 짚고 넘어간다.

버퍼 오버플로우

프로그램이 실행하면서 임시 공간인 버퍼에 입력받는 값이 버퍼를 가득 채우다 못해 넘쳐서 버퍼 이후의 공간을 침범하는 현상을 말한다. C 언어의 표준 함수 중 버퍼 오버플로우에 노출된 strcpy, strcat, gets, sscanf, sprintf 함수 등의 취약점에 해당한다. 이 함수들은 들어오는 문자열의 길이를 제한하지 않기 때문에 복사해서 붙여넣기할 문자열의 길이를 넘치게 복붙해버리면 에러가 난다. 이런 상황을 버퍼 오버플로우.

기타 C언어 관련 정리

*C언어에서는 문자열 끝에 반드시 NULL이 들어있어야 하는 이유:

문자열을 출력하는데 사용하는 서식문자 "%s"를 생각해보자. 만약 이 %s가 존재하지 않는다면? 메모리 상에서 문자열은 이진 데이터로 저장되기 때문에 문자열의 시작과 끝이 표시되어 있지 않다면 문자열을 구분하기가 불가능함(모두 이진 데이터니까 어디까지 문자열로 바꾸고 어디까지 그냥 이진 데이터로 생각해야 하는지 컴퓨터가 모르기 때문). 그래서 NULL 문자로 여기가 문자열의 끝이오~하고 표시해주는 것.(출처)

*C에서 문자열/문자를 나타낼 떄 큰따옴표/작은따옴표 구분이 중요한 이유:

이번 프로젝트에서 굉장히 크게 배운 것 중 하나. 긴말 할 것 없이 예시로 보자.

반응형