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 |