336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
소켓 통신 #3 - ICMP 클라이언트 구현하기(3)
- by Tapito
2편에서 ICMP 패킷을 sendto로 보낼 때는 IP 헤더를 소켓에서 알아서 작성하도록 지정했습니다. 이번에는 IP 헤더를 직접 구성하여 패킷을 보내보도록 하겠습니다. IP 헤더를 직접 작성하기 위해서는 setsockopt 함수에서 IP_HDRINCL 속성을 지정해야 합니다.
/* 예제 */
#pragma comment(lib, "ws2_32.lib")
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#pragma pack(push, 1)
typedef struct _tagIPHDR4
{
BYTE bVersionIHL; // Version and IHL
BYTE bTypeOfService; // Type Of Service
WORD wLength; // Total Packet Length
WORD wIdentification; // Fragment Identification
WORD wFlagOffset; // Flags and Fragment Offset
BYTE bTimeToLive; // Time To Live
BYTE bProtocol; // Protocol
WORD wChecksum; // Checksum
struct in_addr addressSource; // Internet Address - Source
struct in_addr addressDestination; // Internet Address - Destination
} IPHDR4, NEAR * PIPHDR4, FAR * LPIPHDR4;
typedef struct _tagICMPHDR4
{
BYTE bType; // Type
BYTE bCode; // Code
WORD wChecksum; // Checksum
WORD wIdentification; // Identification
WORD wSequence; // Sequence
} ICMPHDR4, NEAR * PICMPHDR4, FAR * LPICMPHDR4;
#pragma pack(pop)
WORD GetChecksumWord(LPBYTE lpByte, SIZE_T nByte)
{
SIZE_T tByte;
DWORD dwTemp = 0;
WORD wReturn;
switch (nByte % 2)
{
case 0: // 짝수 바이트면
for (tByte = 0; tByte < nByte; tByte += 2)
{
dwTemp += MAKEWORD(lpByte[tByte], lpByte[tByte + 1]);
}
dwTemp = LOWORD(dwTemp) + HIWORD(dwTemp);
dwTemp = LOWORD(dwTemp) + HIWORD(dwTemp);
wReturn = (WORD)((~dwTemp) & 0xFFFF);
break;
case 1: // 홀수 바이트면
for (tByte = 0; tByte < nByte - 1; tByte += 2)
{
dwTemp += MAKEWORD(lpByte[tByte], lpByte[tByte + 1]);
}
dwTemp = lpByte[nByte - 1];
dwTemp = LOWORD(dwTemp) + HIWORD(dwTemp);
dwTemp = LOWORD(dwTemp) + HIWORD(dwTemp);
wReturn = (WORD)((~dwTemp) & 0xFFFF);
break;
default:
wReturn = 0;
}
return wReturn;
}
int main(int argc, char * argv[])
{
/* WinSock */
WSADATA wsaData;
SOCKET ipSocket;
/* 출발지와 목적지 주소 */
SOCKADDR_IN ipSourceAddress;
SOCKADDR_IN ipDestinationAddress;
/* select 함수를 사용하기 위한 소켓 목록과 제한 시간 */
fd_set fdset;
TIMEVAL timeValue;
/* 보내는 패킷와 받은 패킷이 저장될 구조체 */
struct { IPHDR4 ipHeader; ICMPHDR4 icmpHeader; } icmpRequest;
struct { IPHDR4 ipHeader; ICMPHDR4 icmpHeader; } icmpResponse;
/* 소켓 함수의 실행 결과 */
int iReturn = 0;
/* recvfrom 함수를 사용하기 위한 변수 선언: 패킷의 발신지 주소와 그 크기가 저장될 곳 */
SOCKADDR_IN ipSocketAddress;
int nSocketAddress = sizeof(ipSocketAddress);
/* setsockopt를 사용하기 위한 변수 */
DWORD sockopt = 1;
/* WinSock 초기화 */
WSAStartup(MAKEWORD(1, 1), &wsaData);
ipSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
/* 패킷을 보낼 때 IP 헤더까지 직접 편집할 것임을 지정합니다. */
setsockopt(ipSocket, IPPROTO_IP, IP_HDRINCL, (const char *)&sockopt, sizeof(sockopt));
/* 자신의 IP 주소 */
ZeroMemory(&ipSourceAddress, sizeof(ipSourceAddress));
ipSourceAddress.sin_family = AF_INET;
ipSourceAddress.sin_port = 0;
ipSourceAddress.sin_addr.s_addr = inet_addr("192.168.0.3");
/* 패킷을 보낼 대상 */
ZeroMemory(&ipDestinationAddress, sizeof(ipDestinationAddress));
ipDestinationAddress.sin_family = AF_INET; // 인터넷 망에 접속
ipDestinationAddress.sin_port = 0; // 포트 번호: 0번
ipDestinationAddress.sin_addr.s_addr = inet_addr("173.194.127.212"); // 구글 www.google.com의 IP 주소
/* 보낼 패킷 구성 */
ZeroMemory(&icmpRequest, sizeof(icmpRequest));
icmpRequest.icmpHeader.bType = 8;
icmpRequest.icmpHeader.bCode = 0;
icmpRequest.icmpHeader.wChecksum = 0;
icmpRequest.icmpHeader.wIdentification = htons(1);
icmpRequest.icmpHeader.wSequence = htons(1);
icmpRequest.icmpHeader.wChecksum = GetChecksumWord((LPBYTE)&icmpRequest, sizeof(icmpRequest));
icmpRequest.ipHeader.bVersionIHL = 0x45;
icmpRequest.ipHeader.bTypeOfService = 0x00;
icmpRequest.ipHeader.wLength = sizeof(icmpRequest);
icmpRequest.ipHeader.wIdentification = 1;
icmpRequest.ipHeader.wFlagOffset = 0x00;
icmpRequest.ipHeader.bTimeToLive = 128;
icmpRequest.ipHeader.bProtocol = 1; // IP 헤더 이후 ICMP 헤더가 붙음을 명시
icmpRequest.ipHeader.wChecksum = 0;
icmpRequest.ipHeader.addressSource = ipSourceAddress.sin_addr;
icmpRequest.ipHeader.addressDestination = ipDestinationAddress.sin_addr;
icmpRequest.ipHeader.wChecksum = GetChecksumWord(&icmpRequest, sizeof(icmpRequest));
/* 패킷 보내기 */
iReturn = sendto(ipSocket, (LPSTR)&icmpRequest, sizeof(icmpRequest), 0, (SOCKADDR *)&ipDestinationAddress, sizeof(struct sockaddr_in));
printf("sendto = %d\n", iReturn);
/* 에코 수신 대기 */
FD_ZERO(&fdset);
FD_SET(ipSocket, &fdset);
timeValue.tv_sec = 1;
timeValue.tv_usec = 500;
iReturn = select(1, &fdset, NULL, NULL, &timeValue); // -1: 오류 발생, 0: 타임아웃, 양수: 송수신 가능한 소켓의 개수
printf("select = %d\n", iReturn);
/* 에코 수신 */
iReturn = recvfrom(ipSocket, (LPSTR)&icmpResponse, sizeof(icmpResponse), 0, (SOCKADDR *)&ipSocketAddress, &nSocketAddress); // 받기
printf("recvfrom = %d TTL = %d\n", iReturn, icmpRequest.ipHeader.bTimeToLive);
closesocket(ipSocket);
WSACleanup();
return 0;
}
실행 결과는 다음과 같습니다.
'Programming Language > C&C++' 카테고리의 다른 글
| SAFEARRAY의 간단한 사용법 정리 (0) | 2017.08.06 |
|---|---|
| [소켓 프로그래밍]UDP 브로드캐스트에 의한 데이터 보내기 및 받기 (0) | 2017.08.05 |
| 윈도우에서 IP 주소 얻는 방법 (0) | 2014.11.13 |
| 소켓 통신 #3 - ICMP 클라이언트 구현하기(2) (0) | 2014.11.11 |
| 소켓 통신 #3 - ICMP 클라이언트 구현하기(1) (0) | 2014.11.11 |