Winsock 프로그래밍 연습 - 2 : 삽질
코드 받기
원본 주소
http://www.exegesis.uklinux.net/gandalf/winsock/winsock1.htm
원본 코드
/*
* whois.c
*
* (c) Feb 2000 by
Gandalf
*
*/
#include <stdio.h>
#include
<stdlib.h>
#include <time.h>
#include
<windows.h>
#include <winsock.h>
#define
WIN32_LEAN_AND_MEAN /* define win 32 only */
void
handle_error(void); /* Error handler routine */
void
write_file(char *buf); /* Write details to log file */
int main
(int argc, char **argv)
{
WORD wVersionRequested; /* socket dll
version info */
WSADATA wsaData; /* data for socket lib
initialisation */
SOCKET sock; /* socket details
*/
const int BUF_LEN=10000; /* Buffer size for transfers */
struct sockaddr_in address; /* socket address stuff */
struct hostent
* host; /* host stuff */
int err; /*
error trapping */
float socklib_ver; /* socket dll version
*/
char File_Buf[BUF_LEN]; /* file buffer */
char
DomainName[100]; /* domain name from user */
char HostName[100];
/* host name from user */
time_t now; /*
for date and time */
if (argc < 2) /* check we
have command line options */
{
printf("\nUseage: whois domainname
[whois.server]\n");
exit(0);
}
strcat(DomainName, argv[1]);
/* get domain name from command line */
strcat(DomainName, "\r\n"); /* add
crlf as whois servers expect it */
if (argc == 3)
strcat(HostName, argv[2]); /* get host name from command line */
else
strcat(HostName, "whois.internic.net");
wVersionRequested
= MAKEWORD( 1, 1 );
/*
* We need to call the WSAStartup
routine BEFORE we try to use any of
* the Winsock dll calls.
*/
if ( WSAStartup( wVersionRequested, &wsaData ) != 0 )
handle_error();
/* Check socket DLL supports 1.1 or higher
*/
socklib_ver = HIBYTE( wsaData.wVersion ) / 10.0;
socklib_ver
+= LOBYTE( wsaData.wVersion );
if ( socklib_ver < 1.1 )
{
printf ("\nError: socket library must support 1.1 or
greater.\n");
WSACleanup(); /* clean up before exit */
exit(0);
}
/* write current date and time to log file and screen
*/
time(&now);
sprintf(File_Buf, "Whois Session Started
%.24s.\n\n", ctime(&now));
write_file(File_Buf);
/*
*
Open a socket. The AF_INET parameter tells windows we want to use the
*
internet. Other parameters for different networking can be chosen e.g.
*
for netbios, IPX etc. The SOCK_STREAM parameter lets windows know we want
* to use TCP rather than UDP, and the final parameter will always be
*
zero for what we want to do and tells windows to use whatever
* default
communication protocol has been established (eg PPP and IP)
*/
if ( (sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET )
handle_error();
/* We now need to initialise a couple of variables in
the address
* structure. Once again, to tell windows we are using the
internet,
* and also what port we want to use when connecting to a
remote
* computer. In this case it is port 43 which is the standard port
for
* whois. The htons routine is used to convert the way Intel
chips
* store data in memory, which is different compared to many other
computers.
* The standard is based on Motorola format.
*/
address.sin_family=AF_INET; /* internet */
address.sin_port = htons(43); /* port 43 for whois */
/* write to
the log file and screen */
sprintf(File_Buf,"Connecting to %s\n",
HostName);
write_file(File_Buf);
/*
* host is a pointer
to a structure of the predefined type hostent. We
* need to call
gethostbyname with the DNS name we want to use to return
* a pointer to a
hostent structure. This is so we can resolve an IP
* address from our
ISP's nameserver.
*/
if ( (host=gethostbyname(HostName)) ==
NULL )
handle_error();
/* we then initialise the address
structure with the resolved IP address
*/
address.sin_addr.s_addr=*((unsigned long *)
host->h_addr);
/* Now we're ready to actually connect to the whois
server itself */
if ( (connect(sock,(struct sockaddr *) &address,
sizeof(address))) != 0)
handle_error();
/*
* We
should be connected to the whois server at this point
* so we need to
send the domain name and wait for the response. The send
* and recv
routines are always used with TCP. These enable handshaking
* compared
to the sendto and recvfrom routines which are used for UDP
* protocol,
ie without handshaking.
*/
strcpy(File_Buf,
DomainName);
err=send(sock,File_Buf,strlen(File_Buf),0); /* send domain
name */
err=recv(sock,File_Buf,BUF_LEN,0); /* discard first
response */
err=recv(sock,File_Buf,BUF_LEN,0); /* get query
results back */
write_file(File_Buf);
/* Always call
WSACleanup before exiting */
WSACleanup(); /* clean up before exit
*/
exit(0);
}
void
handle_error(void)
{
/*
* Errors are handled by calling the
WSAGetLastError routine which
* will return the last error as one of
the following. As we develop
* this tutorial, we will go into much more
detail on what they mean
* and what caused
them.
*/
switch ( WSAGetLastError() )
{
case
WSANOTINITIALISED :
printf("Unable to initialise
socket.\n");
break;
case WSAEAFNOSUPPORT
:
printf("The specified address family is not
supported.\n");
break;
case WSAEADDRNOTAVAIL
:
printf("Specified address is not available from the local
machine.\n");
break;
case WSAECONNREFUSED
:
printf("The attempt to connect was forcefully rejected.\n");
break;
case WSAEDESTADDRREQ :
printf("address
destination address is required.\n");
break;
case WSAEFAULT
:
printf("The namelen argument is
incorrect.\n");
break;
case WSAEINVAL :
printf("The
socket is not already bound to an address.\n");
break;
case
WSAEISCONN :
printf("The socket is already
connected.\n");
break;
case WSAEADDRINUSE
:
printf("The specified address is already in
use.\n");
break;
case WSAEMFILE :
printf("No more
file descriptors are available.\n");
break;
case WSAENOBUFS
:
printf("No buffer space available. The socket cannot be
created.\n");
break;
case WSAEPROTONOSUPPORT
:
printf("The specified protocol is not
supported.\n");
break;
case WSAEPROTOTYPE
:
printf("The specified protocol is the wrong type for this
socket.\n");
break;
case WSAENETUNREACH :
printf("The network can't be reached from this host at this
time.\n");
break;
case WSAENOTSOCK :
printf("The
descriptor is not a socket.\n");
break;
case WSAETIMEDOUT
:
printf("Attempt timed out without establishing a
connection.\n");
break;
case WSAESOCKTNOSUPPORT
:
printf("Socket type is not supported in this address
family.\n");
break;
case WSAENETDOWN
:
printf("Network subsystem
failure.\n");
break;
case WSAHOST_NOT_FOUND
:
printf("Authoritative Answer Host not
found.\n");
break;
case WSATRY_AGAIN
:
printf("Non-Authoritative Host not found or
SERVERFAIL.\n");
break;
case WSANO_RECOVERY
:
printf("Non recoverable errors, FORMERR, REFUSED,
NOTIMP.\n");
break;
case WSANO_DATA :
printf("Valid
name, no data record of requested type.\n");
break;
case
WSAEINPROGRESS :
printf("address blocking Windows Sockets operation
is in progress.\n");
break;
case WSAEINTR
:
printf("The (blocking) call was canceled via
WSACancelBlockingCall().\n");
break;
default
:
printf("Unknown
error.\n");
break;
}
WSACleanup();
exit(0);
}
void
write_file(char *buf)
{
/* writes results to a log file and also to the
screen */
FILE
*fp=fopen("whoislog.txt","a+");
fprintf(fp,"%s\n",buf);
fclose(fp);
printf("%s\n",buf);
}
한글로 번역한 코드
/*
whois.c
(c) feb 2000 by Gandalf
다시 치기 http://cakel.tistory.com
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#include <winsock.h>
#define WIN32_LEAN_AND_MEAN // define win32 only
void handle_error(void);
void write_file(char* buf);
int main(int argc, char** argv)
{
WORD wVersionRequested; // 소켓 dll 버전 정보
WSADATA wsaData; // 소켓 lib 생성화를 위한 자료
SOCKET sock; // 소켓 정보
const int BUF_LEN = 10000; // 전송을 위한 크기 설정
struct sockaddr_in address; // 소켓 주소
struct hostent* host; // 호스트
int err; // 문제 코드 잡기
float socklib_ver; // 소켓 DLL 버전
char File_Buf[BUF_LEN]; // 파일 버퍼
char DomainName[100]; // 사용자 도메인 이름
char HostName[100]; // 사용자 호스트 이름
time_t now; // 날짜와 시간
if (argc < 2) // 실행 옵션을 가지고 있는지 확인
{
printf("\n사용법 : whois 도메인명 [whois.server] \n");
exit(0);
}
strcpy(DomainName, argv[1]); // 실행줄에서 도메인 이름 얻기
strcat(DomainName, "\r\n"); // 서버가 예상하는 Carrage Return , Line Feed 를 넣어 준다.
if (argc == 3)
strcpy(HostName, argv[2]); // 명령줄에서 호스트명을 얻습니다.
else
strcpy(HostName, "whois.internic.net");
wVersionRequested = MAKEWORD(1, 1);
// 만약 Winsock dll 호출을 해야한다면 WSAStartup 루틴을 불러와야 합니다.
if( WSAStartup( wVersionRequested, &wsaData ) != 0 )
handle_error();
// 소켓 DLL 이 1.1 또는 이상인지 확인 합니다.
socklib_ver = (float)(HIBYTE( wsaData.wVersion ) / 10.0);
socklib_ver += LOBYTE( wsaData.wVersion );
if ( socklib_ver < 1.1 )
{
printf("\n오류 : 소켓 라이브러리 버전이 1.1 이상이여야 합니다.\n");
WSACleanup(); // 나가기 전에 정리
exit(0);
}
// 현재 시간과 날짜를 화면에 기록합니다
time(&now);
sprintf(File_Buf, "Whois 세션 시작 %.24s.\n\n", ctime(&now));
write_file(File_Buf);
/*
소켓을 엽니다. AF_INET 파라메터는 윈도우즈에게 우리가 인터넷 요구한다는 것을
알려줍니다. 다른 파라메터는 서로 다른 네트워크가 선택되었을 때를 위함입니다.
예) NetBIOS, IPX 등. SOCK_STREAM 파라메터는 윈도우즈에게 TCP 를 UDP 보다더 원한다
는 것을 알려줍니다. 그리고 마지막 파라메터는 항상 0 으로 정해서 윈도우즈에게
기본적으로 전송할 프로토콜이 어떤건지 알려 줍니다 (예 PPP, IP)
*/
if( sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ) == INVALID_SOCKET )
handle_error();
/*
우리는 이제 주소 구조에 있는 몇몇 변수들을 생성해야 합니다. 다시 한번 윈도우즈에게
우리는 인터넷을 쓰고 우리가 원격지에 있는 컴퓨터와 접속시 쓸 포트 번호를 말해줍니다.
이번 경우 43 포트가 쓰이는데 이는 whois 를 위한 표준 포트 입니다. htons 루틴은
인텔 계열 칩의 메모리 저장 방식을 다른 컴퓨터들과 맞게 하기 위해서 변환하는데 쓰입니다.
이 표준은 모토로라 형식에 근간합니다.
*/
address.sin_family = AF_INET; // 인터넷
address.sin_port = htons(43); // whois 를 위한 포트 43
// 로그 파일과 화면에 기록
sprintf(File_Buf, "%s 에 접속합니다\n", HostName);
write_file(File_Buf);
/*
host 는 hostent 형으로 미리 정의된 구조체의 포인터입니다. 우리는 gethostbyname 에
DNS 주소를 써서 hostent 구조체의 주소를 받습니다. 이는 우리가 IP 주소를 ISP 네임서버
에서 받아올 수 있게 하기 위해서 입니다.
*/
if( (host = gethostbyname(HostName)) == NULL )
handle_error();
if( (connect(sock, (struct sockaddr*)&address, sizeof(address)) ) < 0)
handle_error();
// 우리는 받은 IP 주소로 주소 구조체를 시작합니다.
address.sin_addr.s_addr = *((unsigned long*) host->h_addr_list);
// 이제 우리는 실제 whois 서버 자체로 접속할 준비가 되었습니다.
/* 우리는 이 시점에서 whois 서버에 접속을 해야 합니다. 그리고 domain 이름을 보내고
응답을 기다립니다. send 와 recv 루틴이 항상 TCP 와 함께 사용됩니다. 이들은 헨드셰이킹
을 하는데 sendto 와 recvfrom 루틴을 쓰는 UDP 프로토콜(헨드세이킹을 하지 않습니다)
과 같습니다.
*/
strcpy(File_Buf, DomainName);
err = send(sock, File_Buf, strlen(File_Buf), 0); // 도메인 이름을 보냅니다.
err = recv(sock, File_Buf, BUF_LEN, 0); // 첫번째 응답을 버립니다.
err = recv(sock, File_Buf, BUF_LEN, 0); // 돌아오는 질의의 결과를 받습니다.
write_file(File_Buf);
// 항상 나가기 전에 WSACleanup 을 수행합니다
WSACleanup(); // 나가기 전에 청소
exit(0);
return 0;
}
void handle_error(void)
{
/*
Error 는 WSAGetLastError 루틴을 써서 받아 오는 최근의 오류를 받습니다.
이 교제 배우면서 우리는 좀더 무엇 때문에 이 오류가 나는지 배우게 될 것입니다
*/
switch( WSAGetLastError() )
{
case WSANOTINITIALISED :
printf("소켓을 시작 할수가 없습니다. \n");
break;
case WSAEAFNOSUPPORT :
printf("이 주소 계열은 지원하지 않습니다.\n");
break;
case WSAEADDRNOTAVAIL :
printf("이 주소는 로컬 컴퓨터에서 할당 받을수 없습니다.\n");
break;
case WSAECONNREFUSED :
printf("이 시도는 의도적으로 거절당했습니다.\n");
break;
case WSAEDESTADDRREQ :
printf("목적지 주소가 필요합니다.\n");
break;
case WSAEFAULT :
printf("namelen 인수가 틀립니다.\n");
break;
case WSAEINVAL :
printf("소켓이 아직 그 주소로 가지(bound) 않았습니다.\n");
break;
case WSAEISCONN :
printf("소켓이 이미 연결 되었습니다.\n");
break;
case WSAEADDRINUSE :
printf("그 주소는 이미 사용 중입니다.\n");
break;
case WSAEMFILE :
printf("더 이상 기술자 파일이 존재 하지 않습니다\n");
break;
case WSAENOBUFS :
printf("더 이상 버퍼 공간이 있지 않습니다. 소켓이 만들어 질수 없습니다.\n");
break;
case WSAEPROTONOSUPPORT :
printf("명시한 프로토콜은 지원하지 않습니다.\n");
break;
case WSAEPROTOTYPE :
printf("명시한 프로토콜은 이 소켓과 맞지 않은 형식입니다.\n");
break;
case WSAENETUNREACH :
printf("지금 시간에는 이 호스트에서 네트워크에 접근 할수가 없습니다.\n");
break;
case WSAENOTSOCK :
printf("기술자가 소켓이 아닙니다.\n");
break;
case WSAETIMEDOUT :
printf("접속이 되지 않은 시도의 시간이 초과되었습니다.\n");
break;
case WSAENETDOWN :
printf("네트워크 하부시스템이 망가졌습니다.\n");
break;
case WSAHOST_NOT_FOUND:
printf("인증 응답 호스트가 발견되지 않았습니다.\n");
break;
case WSATRY_AGAIN :
printf("비-인증 호스트가 없거나 서버가 실패(SERVERFAIL)했습니다.\n");
break;;
case WSANO_RECOVERY :
printf("되돌릴수 없는 에러 입니다. FORMERR, REFUSED, NOTIMP.\n");
break;
case WSANO_DATA :
printf("유요한 이름이지만 요구된 형식의 자료가 없습니다.\n");
break;
case WSAEINPROGRESS :
printf("주소 막기 윈도우즈 소켓 작업이 진행 중입니다.\n");
break;
case WSAEINTR :
printf("(막힌) 호출이 WSACancelBlockingCall() 함수에 의해 취소 되었습니다.\n");
break;
default :
printf("알수 없는 오류 입니다.\n");
break;
}
WSACleanup();
exit(0);
}
void write_file(char *buf)
{
// 화면과 파일에 동시에 출력합니다.
FILE *fp = fopen("whoislog.txt", "a+");
fprintf(fp, "%s\n", buf);
fclose(fp);
printf("%s\n",buf);
}
컴파일시 주의사항
프로젝트 옵션에서 Link 탭, Object/library modules 에 ws2_32.lib 를 추가(Lib 링크 오류)하시고 Project Options 에 보시면 ws2_32.lib /nologo /subsystem:console 로 맞춰 주시야지 Link 오류(WinMain16 링크 오류)가 생기지 않습니다.
실행 결과 : 원본 코드 / 친 코드 모드 작동되지 않았습니다. Finger 서버 자체에 접속이 안되었습니다. 컴파일 환경이 달라서 그런거 같습니다.