일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 비박스
- reversing
- bee-box
- Webhaking
- 시스템해킹
- XSS
- 워게임
- 소프트웨어보안
- 웹
- 해킹
- 모의해킹
- webhacking
- 드림핵
- 소프트웨어
- WarGame
- System
- 순서도
- CodeEngn
- 시스템
- 네트워크
- Web
- 웹해킹
- 알고리즘
- ftz
- dreamhack
- hacking
- 리버싱
- 네트워크보안
- TCP
- network
- Today
- Total
Without a Break
BSD Sockets API-based Network Programming 본문
파일 입출력
파일모드 | 기능 | 설명 |
"r" | 읽기 전용 | 파일을 읽기 전용으로 연다. 단, 파일이 반드시 있어야 한다. |
"w" | 쓰기 전용 | 새 파일을 생성한다. 만약 파일이 있으면 내용을 덮어쓴다. |
"a" | 추가 | 파일을 열어 파일 끝에 값을 이어 쓴다. 만약 파일이 없으면 파일을 생성한다. |
"r+" | 읽기/쓰기 | 파일을 읽기/쓰기 용으로 연다. 단, 파일이 반드시 있어야 하며 파일이 없으면 NULL을 반환한다. |
"w+" | 읽기/쓰기 | 파일을 읽기/쓰기 용으로 연다. 파일이 없으면 파일을 생성하고, 파일이 있으면 내용을 덮어쓴다. |
"a+" | 추가(읽기/쓰기) | 파일을 열어 파일 끝에 값을 이어 쓴다. 만약 파일이 없으면 파일을 생성한다. 읽기는 파일의 모든 구간에서 가능하지만, 쓰기는 파일의 끝에서만 가능하다. |
"t" | 텍스트 모드 | 파일을 읽거나 쓸 때 개행 문자 \n과 \r\n을 서로 변환한다. ^Z 파일의 끝으로 인식하므로 ^Z까지만 파일을 읽는다. |
"b" | 바이너리 모드 | 파일의 내용을 그대로 읽고, 값을 그대로 쓴다. |
*"t"와 "b"는 Windows전용
1. 서식을 지정하여 파일에 문자열 읽기
#define _CRT_SECURE_NO_WARNINGS //fopen 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h> //fopen, fscanf, fclose 함수가 선언된 헤더 파일
int main()
{
char s1[10];
int num1;
FILE *fp = fopen("hello.txt", "r"); //hello.txt 파일을 읽기 모드(r)로 열기
// 파일 포인터 반환
fscanf(fp, "%s %d", s1, &num1); //서식을 지정하여 파일에서 문자열 읽기
printf("%s %d\n", s1, num1); //Hello 100: 파일에서 읽은 값을 출력
fclose(fp); //파일 포인터 닫기
return 0;
}
2. 제한된 버퍼로 파일 전체 읽기
#define _CRT_SECURE_NO_WARNINGS //fopen 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h> //fopen, feof, fread, fclose 함수가 선언된 헤더 파일
#include <string.h> //strlen, memset 함수가 선언된 헤더 파일
int main()
{
char buffer[5] = {0, }; //문자열 데이터 4바이트 NULL 1바이트. 4+1=5
int count = 0;
int total = 0;
FILE *fp = fopen("hello2.txt", "r"); //hello2.txt 파일을 읽기 모드(r)로 열기
//파일 포인터 반환
while(feof(fp) == 0)
{
count = fread(buffer, sizeof(char), 4, fp); //1바이트씩 4번(4바이트) 읽기
printf("%s", buffer); //읽은 내용 출력
memset{buffer, 0, 5) //버퍼를 0으로 출력
total += count; //읽은 크기 누적
}
printf("\ntotal: %d\n", total): //total: 13: 파일을 읽은 전체 크기 출력
fclose(fp); //파일 포인터 닫기
return 0;
}
3. 파일에서 구조체 읽고 쓰기
3-1.ANSI C 표준
앞에 제시한 코드들은 텍스트 형식의 파일로부터 문자열을 읽는다고 가정하였으나, 현재 많은 파일들은 바이너리 형식으로 작성되어 있다. (바이너리 파일이 공간 활용 효율이 훨씬 좋기 때문)
=> 실무에서는 구조체를 활용하여 바이너리 파일을 처리함.
(단, 호환성이 중요한 웹 기술에선 문자열을 이용해 정보를 저장하는 경우도 많음)
<stdio.h>에 정의된, ANSI C 표준 라이브러리의 파일 입출력 함수들은 FILE이라는 구조체를 fopen()을 이용해 메모리 상에 생성한 뒤, 그 시작 주소를 리턴값으로 받아 쓰는 파일 포인터 FILE *를 참조하는 함수들임
- UNIX 스타일의 파일 입출력이 마음에 들지 않았던 표준 라이브러리 설계자들이 사용자 프로세스의 공간(heap 영역)에 FILE 구조체를 할당해서 직접 관리하는 접근을 사용하였음.
- 파일의 포인터, 구조체를 사용하는 이유 : 서로 다른 운영체제를 고려한 것
3-2. 유닉스 스타일
UNIX에서는 포인터 대신 int형인 file descriptor(fd)를 사용해 파일 접근
- 커널은 프로세스 별로 프로세슷 제어 블록(PCB)라는 공간에 fd와 파일의 포인터를 기록한 file descriptor 테이블을 유지
ANSI C 스타일이 파일에 대한 정보를 구조체의 형태로 사용자 프로그램에 노출시키는 반면, 유닉스 스타일은 번호표만 제공하여 실제 일은 커널이 함
BSD SOCKETS
1. 유닉스(UNIX)와 유닉스 계열(Unix-like) 운영체제
유닉스(UNIX)
- 1969년 AT&T Bell Laboratory에서 일하는 Dennis Ritchie와 Ken Thompson이 만든 운영체제
- AT&T(현재는 The Open Group)에서 해당 용어를 UNIX라는 트레이드마크로 등록해서 사용을 제한하였기 때문에, 사용 허락을 따로 맡지 않았다면 Unix로 기재하는 게 바람직함
유닉스 계열 운영체제 (Unix-like System)
- AT&T에 라이선스 비용을 내고 코드를 사서 자체 운영체제로 개조함
- 리누스 토발즈가 처음부터 새로 작성한, 유닉스의 기능을 흉내낸 운영체제
1-1. 유닉스 계열 운영체제의 표준화 작업
여러 운영체제의 난립으로 인해 여러 단체에서 프로그래밍 인터페이스를 표준화함
2. 네트워크 프로그래밍과 소켓 프로그래밍
2-1. 소켓 프로그래밍이란?
유닉스 계열 운영체제인 BSD에 포함된 TCP/IP 네트워킹 구현에서 사용된 BSD Sockets Application PRogramming Interface(API)를 바탕으로 한 네트워크 프로그래밍
현재까지 쓰이는 사실 상 모든 소켓 API는 BSD Socket API 또는 BSD Sockets API의 표준화 버전인 POSIX Sockets API를 따르고 있음
2-2. 소켓 및 BSD 소켓이란?
Socket : 특정 포트 식별자를 포함하는 주소, 즉 인터넷 주소와 TCP 포트의 연결
- 소켓을 네트워크 주소(IP주소)와 호스트 주소(포트 번호)를 연접(concatenate)한 것으로 정의됨
- 한 쌍의 소켓이 각 TCP 연결을 유일하게 특정함
- 프로세스 간 통신에 사용되는 일시적인 개체이며, 일부 프로세스가 소켓을 참조하는 디스크립터를 보유할 경우에만 존재
- 소켓은 소켓 시스템 콜에 의해 생성되며, 소켓에 대한 디스크립터를 반환
- 통신 도메인과 주소를 담는 구조체의 형태로 구성되어 있음
- 소켓은 소켓 디스크립터로 특정되는 임시적으로 존재하는 객체임
2-3. 소켓이란?
소켓 : 컴퓨터 네트워크를 통한 프로세스 간 통신 흐름의 endpoint
- Local socket address : 로컬 IP주소와 포트 번호
- Remote socket address : TCP 서버가 여러 클라이언트를 동시에 서비스할 수 있으므로 설정된 TCP 소켓에만 해당함. 서버는 각 클라이언트 당 하나의 소켓이 생성하고 이 소켓들은 같은 로컬 소켓 주소를 공유함
- Protocol : 전송 프로토콜 ex)TCP, UDP
소켓 API : 응용 프로그램을 제어하고 네트워크 소켓을 사용하는 것을 허락함. 주로 운영체제에서 제공
3. Windows Sockets API(WSA, Winsock)
3-1. 윈도우의 네트워킹
윈도우는 산업계에서 요구했던 여러 종류의 네트워킹 API를 제공하고 있음 (이 중 일부는 유닉스 계열 운영체제에서도 지원됨)
- Windows Sockets API(Winsock) : 응용 프로그램이 BSD Sockets API 스타일로 네트워크를 활용하기 위한 API
- Winsock Kernel(WSK) : 커널 모듈이 네트워크를 활용하기 위한 API
- Remote procedure call(RPC) : 분산 컴퓨팅 환경에서 사용되는 네트워크
- Web accress APIs : HTTP, FTP, Gopher 등의 인터넷 응용 계층에 대한 네트워킹 API 제공
- NetBIOS : 예전 IBM PC에서 Local Area Network (LAN)에 접속하기 위해 만들어진 네트워킹 API
3-2. Windows Sockets API의 특징
- API를 사용하는 코드 상단에 아래 코드를 기재해 명시적으로 정적 라이브러리를 링크해야 함
#pragma comment (lib, "ws2_32.lib")
- 실제 대부분의 Winsock 함수들은 Winsock 동적 라이브러리(WS2_32.DLL)에 정의되어 있으며, 함수 사용 전에 WSAStartup() 함수를 먼저 호출해 동적 라이브러리를 불러오고 (load) 초기화를 해야 함
WSADATA was;
if (WSAStartup (MAKEWORD(2,2), &wsa) != 0)
exit(1);
- Winsock 함수들의 사용이 끝났으면 동적 라이브러리를 unload하는 WSACleanup() 함수를 호출해야 함
- 호출 시 아무런 통보 없이 전송 중인 연결이 끊어지게 되므로, WSACleanup()를 호출하기 전에 각 소켓 연결 종료 시에는 close() 함수 대신 shutdown() 함수 같이 연결 종료를 기다리는 함수를 사용하는게 바람직함
4. Client-Server Model for Socket
4-1.Client-Server Model
- 두 프로세스 간에 통신 채널을 구축하려면 다른 프로세스가 대기하는 동안 한 프로세스가 주도권을 잡고 있어야 함
- 연결을 시작하는 것은 클라이언트이고, 서버는 시작되는 것을 기다림
- peer-to-peer 연결에서, 프로그램은 클라이언트와 서버 모두로 작동할 수 있음
- 클라이언트 : Sometimes on
- 서버 : always on
- 서버는 응용 프로그램에 따라 연결 지향(TCP) 또는 connectionless(UDP)일 수 있음
4-2. Connection-oriented (TCP) vs. Connectionless(UDP)
- socket() 소켓을 만드는 함수
- conenct() : 원격 호스트에 연결하는 함수
- bind() : 소켓에 local address를 할당하고 해당 프로세스를 묶는 함수
- listen() : 소켓을 통해 들어오는 연결을 듣는 함수
- accept() : 연결이 들어왔을 때 이를 받아들이는 함수
- send() : 소켓 파일 디스크립터로 내용을 전송하는 함수
- recv() : 소켓 파일 디스크립터로부터 내용을 읽는 함수
4-3. 서비스를 식별하는 데 사용하는 포트
목적지 포트는 소켓의 유일무이한 식별자임
4-4. 너무 많은 클라이언트가 도착한다면?
서버는 Multiple requests를 위해서 동시 접속을 제공해야 함
- Process-based (fork)
- 새로운 연결을 위해 fork()로 자식 프로세스를 생성
- 단점 : Context switch를 위해 큰 오버헤드가 일어날 수 있음
- Threading
- 각 사용자를 위해 Multiple threads 제공
- 이해가 쉬움
- 단점 : 복잡도 증가로 경쟁 상태 발생 가능, 의도치 않은 데이터 공유 에러 가능성이 있음
- 'Select' function (event-based method)
- 멀티 디스크립터를 모니터하기 위한 소켓 세트 설정
- 동시에 여러 소켓을 다루는 것이 아니라 여러 사용자로부터 하나의 event가 발생 시 이를 처리함
- 명시적 제어 흐름, 경쟁상태 없음
- 명시적 제어 흐름은 더 복잡함
몇 클라이언트는 연결이 block되거나 대기해야할 수 있음
5. BSD Sockets API : Daytime Protocol
5-1. Daytime Protocol
TCP 기반 Daytime 서비스 : 3단계로 이루어짐
① 클라이언트와 서버와 TCP 연결을 맺음 (TCP connection establishment)
- 서버는 클라이언트가 13번 TCP 포트로 접속할거라 가정하고 들음
② 서버는 연결이 맺어졌으면 날짜와 시간 등의 정보를 ASCII 문자열의 형태로 보냄
③ 보낸 뒤에 곧바로 서버가 TCP 연결을 닫음 (TCP connection close)
UDP 기반 Daytime 서비스 : 2단계로 이루어짐
① 클라이언트가 서버에 아무 내용이나 담아서 UDP 데이터그램을 보냄
- 서버는 클라이언트가 13번 UDP 포트로 접속할거라 가정하고 들음
② 서버는 받은 UDP 데이터그램의 내용은 무시하고 날짜오 시간 등의 정보를 ASCII 문자열의 형태로 보냄
5-2. Daytime TCP Client (daytimetcpcli.c)
#include "unp.h"
int main(int argc, char *argv[])
{
int sockfd, n; //sockfd 소켓 디스크립터(번호표), n은 시간 정보의 크기(바이트 단위)
char recvline[MAXLINE+1]; //서버로부터 받을 시간 정보를 저장할 버퍼
struct sockaddr_in servaddr; //서버의 주소를 저장한 인터넷 소켓 주소 구조체
// 시간 서버의 IP주소가 인자로 주어졌는지 체크
if(argc != 2)
err_quit("usage: %s <IPaddres>", argv[0]);
#ifdef _WIN32
WSAStartup(MAKEWORD(2,2), &wsaData); //Windows Sockets API Version 2.2
#endif
//TCP 소켓을 생성해 socket descriptor를 받아와서 sockfd에 저장
if ((sockfd=socket(AF_INET, SOCK_STREA,0)) < 0)
err_sys("socket error");
//서버 주소에 대한 구조체 servaddr에 주소를 설정
bzero(&servaddr, sizeof(servaddr)); //0으로 제공
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(13); //daytime server port number
if (inet_pton(AF_INET, argv[1], &servaddr, sin+addr) <= 0)
err_quit("inet_pton error for %s", argv[1]); //argv[1] 문자열에 저장된 dotted
//decimal 형식의 서버 주소를 32비트 이진수로 변환
//servaddr에 저장된 주소로 연결 시도
if (connect(sockfd, (SA*) &servaddr, sizeof(servaddr)) < 0)
err_sys("connect error");
//연결된 소켓을 통해서버가 보낸 바이트 열 (즉, 읽을 문장)이 있으면
while( (n = read(sockfd, recvline, MAXLINE)) > 0) { //이를 revline에 저장함
recvline[n] = 0; //buffer overflowㄹ르 막기 위해 마지막 글자는 널 캐릭터로 바꿈
if(fputs(revline, stdout) == EOF) //stdout에 revline을 밀어넣음
err_sys("fputs error"); //코드 구조 상 fputs의 결과가 EOF가 나올 수 없음
}
if (n < 0) //정상적이었다면 n==0이어야 하며, n<0이면 while 루프에 문제가 있었음
err_sys("read error");
#ifdef _WIN32
WSAClenaup(); //Winsock 사용 후 정리
#endif
exit(0);
}
5-3. Daytime TCP Server (daytimetcpcli.c)
#include "unp.h"
#include <time.h>
int main(int argc, char *argv[]) {
int listenfd, connfd; //client를 기다리는 listenfd와, 연결 수립 후 사용하는 connfd
char buff[MAXLINE]; //client에 보낼 내용을 저장하는 buffer
struct sockaddr_in servaddr; //server가 기다리려는 client의 주소 범위를 저장함
time_t ticks;
#ifdef _WIN32
WSAStartup(MAKEWORD(2,2), &wsaData); //Windows Sockets API Version 2.2
#endif
//TCP 소켓을 생성해 socket descriptor를 받아와서 sockfd에 저장
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
err_sys("socket error");
//서버 주소에 대한 구조체 servaddr에 주소를 설정함
bzero(&servaddr, sizeof(servaddr)); //0으로 채움
servaddr.sin_family = AF_INET; //Internet Protocol Suite 사용
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //0으로 채우는 것과 동일
servaddr.sin_port = htons(13); //daytime server port number
//serveraddr에 저장된 주소를 소켓에 bind함
if(bind(listenfd, (SA*) &servaddr, sizeof(servaddr)) < 0)
err_sys("bind error");
//공간을 줄이기 위해 socket sockaddr는 SA로 치환함. conect는 SA* 타입을 받아야 하는데 &servaddr는 struct_sockaddr_in* 타입으로 다른 상황이므로, 타입 캐스팅이 필요
//bind한 소켓을 통해 client의 접속을 대기함
int backlog = LISTENQ; //listen 함수가 리턴될 때까지 받을 수 있는 클라이언트의 최대 수
if (listen(listenfd, backlog) < 0)
err_sys("listen error");
for(;;){
again:
if((connfd = accept(listenfd, (SA*) NULL, NULL)) < 0)
if(errno == EPROTO || errno == ECONNABORTED) //SW문제로 연결 중단
goto again;
else
err_sys("accept error");
ticks = time(NULL);
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
//수립된 연결에 대한 socket descriptor인 connfd에 저장된 시간 문자열을 기록
if(write(connfd, buff, strlen(buff)) != strlen(Buff))
err_sys("send error");
if(close(connfd) == -1) //accpet로 얻은 소켓 connfd을 담음
err_sys("close error");
}
#ifdef _WIN32
WSACleanup();
#endif
exit(0);
}
'Network > 네트워크보안과프로그래밍' 카테고리의 다른 글
Firewall (0) | 2022.12.02 |
---|---|
Attacks on the TCP protocol (0) | 2022.11.21 |
TCP Connection Establishment and Termination (0) | 2022.10.07 |
TCP: Transmission Control Protocol (0) | 2022.09.30 |
UDP: User Datagram Protocol (0) | 2022.09.30 |