X Tutup
/* * Copyright (C) 2012 Yee Young Han (http://blog.naver.com/websearch) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "SipPlatformDefine.h" #include "SipTcp.h" #include "MemoryDebug.h" #ifdef WIN32 #pragma comment( lib, "ws2_32" ) #endif #ifndef WIN32 #include /** timeout ÀÌ ÀÖ´Â connect() ÇÔ¼öÀÌ´Ù. * * @ingroup SipStack * @author Yee Young Han * @param iSockFd socket() ÇÔ¼ö·Î ¸¸µé¾îÁø file descriptor * @param saptr connect ÇϰíÀÚÇÏ´Â ¼­¹öÀÇ IP, port ¸¦ ±â·ÏÇÑ sockaddr ±¸Á¶Ã¼ * @param saiLen saptr º¯¼öÀÇ Å©±â * @param iSecond connect °¡ µÇ±â±îÁö ±â´Ù¸®´Â ½Ã°£ ( ÃÊ´ÜÀ§ ) * @return ¼º°øÇϸé 0À» ¸®ÅÏÇÏ°í ½ÇÆÐÇϸé À½¼öÀÇ °ªÀ» ¸®ÅÏÇÑ´Ù. * connect() ÇÔ¼ö È£Ãâ ½ÇÆÐ½Ã¿¡´Â -101¸¦ ¸®ÅÏÇÑ´Ù. * poll() ÇÔ¼ö È£Ãâ ½ÇÆÐ½Ã¿¡´Â -102¸¦ ¸®ÅÏÇÑ´Ù. * getsockopt() ÇÔ¼ö È£Ãâ ½ÇÆÐ½Ã¿¡´Â -103À» ¸®ÅÏÇÑ´Ù. * SO_ERROR °¡ ¹ß»ýÇÑ °æ¿ì -104¸¦ ¸®ÅÏÇÑ´Ù. */ static int ConnectTimeout( int iSockFd, const struct sockaddr *saptr, socklen_t saiLen, int iSecond ) { int iFlags, n, iErrorNum = 0; socklen_t iLen; iFlags = fcntl( iSockFd, F_GETFL, 0 ); fcntl( iSockFd, F_SETFL, iFlags | O_NONBLOCK ); if( ( n = connect( iSockFd, (struct sockaddr *)saptr, saiLen ) ) < 0 ) { // QQQ : errno °¡ 17 ÀÌ °è¼Ó ³ª¿À¸é¼­, Á¦´ë·Î ½ÇÇàµÇÁö ¾Ê´Â °æ¿ì°¡ ÀÖ¾ú´Ù. // À̸¦ ÆÐÄ¡Çϱâ À§Çؼ­ ¾Æ·¡¿Í °°ÀÌ EEXIST ¸¦ ¹«½ÃÇϵµ·Ï ¼öÁ¤ÇÏ¿´´Ù. // QQQ : errno °¡ 4 °¡ ³ª¿Í¼­ ¹®Á¦°¡ µÇ´Â °æ¿ì°¡ ÀÖ´Ù. ±×·¡¼­, À̸¦ ÆÐÄ¡ÇÔ. if( errno != 0 && errno != EINPROGRESS && errno != EEXIST && errno != EINTR ) return -101; } if( n == 0 ) goto done; pollfd sttPoll[1]; sttPoll[0].fd = iSockFd; sttPoll[0].events = POLLIN | POLLOUT; sttPoll[0].revents = 0; n = poll( sttPoll, 1, iSecond * 1000 ); if( n < 0 ) { return -102; } else if( n == 0 ) { errno = ETIMEDOUT; return -102; } if( sttPoll[0].revents & ( POLLIN | POLLOUT ) ) { iLen = sizeof(iErrorNum); if( getsockopt(iSockFd, SOL_SOCKET, SO_ERROR, &iErrorNum, &iLen) < 0 ) return -103; } else { // iErrorNum log } done: fcntl( iSockFd, F_SETFL, iFlags ); // restore if( iErrorNum ) { errno = iErrorNum; return -104; } return 0; } #endif /** * @ingroup SipPlatform * @brief È£½ºÆ® À̸§À¸·Î IP ÁÖ¼Ò¸¦ °Ë»öÇÑ´Ù. * @param szHostName È£½ºÆ® À̸§ * @param szIp IP ÁÖ¼Ò¸¦ ÀúÀåÇÒ º¯¼ö * @param iLen IP ÁÖ¼Ò¸¦ ÀúÀåÇÒ º¯¼öÀÇ Å©±â * @returns ¼º°øÇϸé true ¸¦ ¸®ÅÏÇÏ°í ±×·¸Áö ¾ÊÀ¸¸é false ¸¦ ¸®ÅÏÇÑ´Ù. */ bool GetIpByName( const char * szHostName, char * szIp, int iLen ) { struct hostent * hptr; if( (hptr = gethostbyname(szHostName)) == NULL ) return false; #ifdef WINXP snprintf( szIp, iLen, "%s", inet_ntoa( *(struct in_addr *)hptr->h_addr_list[0] )); #else inet_ntop( AF_INET, (struct in_addr *)hptr->h_addr_list[0], szIp, iLen ); #endif return true; } /** * @ingroup SipPlatform * @brief TCP ¼­¹ö¿¡ ¿¬°áÇÑ´Ù. * @param pszIp TCP ¼­¹ö IP ÁÖ¼Ò * @param iPort TCP ¼­¹ö Æ÷Æ® ¹øÈ£ * @param iTimeout ¿¬°á timeout ½Ã°£ ( ÃÊ´ÜÀ§ ) - 0 ÀÌ»óÀ¸·Î ¼³Á¤ÇØ¾ß ¿¬°á timeout ±â´ÉÀÌ µ¿ÀÛÇÑ´Ù. * @returns ¼º°øÇÏ¸é ¿¬°áµÈ TCP ¼ÒÄÏÀ» ¸®ÅÏÇÏ°í ±×·¸Áö ¾ÊÀ¸¸é INVALID_SOCKET ¸¦ ¸®ÅÏÇÑ´Ù. */ Socket TcpConnect( const char * pszIp, int iPort, int iTimeout ) { char szIp[INET6_ADDRSTRLEN]; Socket fd; memset( szIp, 0, sizeof(szIp) ); if( isdigit(pszIp[0]) == 0 ) { // if first character is not digit, suppose it is domain main. GetIpByName( pszIp, szIp, sizeof(szIp) ); } else { snprintf( szIp, sizeof(szIp), "%s", pszIp ); } #ifndef WINXP if( strstr( szIp, ":" ) ) { struct sockaddr_in6 addr; // connect server. if( ( fd = socket( AF_INET, SOCK_STREAM, 0 )) == INVALID_SOCKET ) { return INVALID_SOCKET; } addr.sin6_family = AF_INET6; addr.sin6_port = htons(iPort); inet_pton( AF_INET6, szIp, &addr.sin6_addr ); #ifndef WIN32 if( iTimeout > 0 ) { if( ConnectTimeout( fd, (struct sockaddr *)&addr, sizeof(addr), iTimeout ) != 0 ) { closesocket( fd ); return INVALID_SOCKET; } } else #endif if( connect( fd, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR ) { closesocket( fd ); return INVALID_SOCKET; } } else #endif { struct sockaddr_in addr; // connect server. if( ( fd = socket( AF_INET, SOCK_STREAM, 0 )) == INVALID_SOCKET ) { return INVALID_SOCKET; } addr.sin_family = AF_INET; addr.sin_port = htons(iPort); #ifdef WINXP addr.sin_addr.s_addr = inet_addr( szIp ); #else inet_pton( AF_INET, szIp, &addr.sin_addr.s_addr ); #endif #ifndef WIN32 if( iTimeout > 0 ) { if( ConnectTimeout( fd, (struct sockaddr *)&addr, sizeof(addr), iTimeout ) != 0 ) { closesocket( fd ); return INVALID_SOCKET; } } else #endif if( connect( fd, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR ) { closesocket( fd ); return INVALID_SOCKET; } } return fd; } /** * @ingroup SipPlatform * @brief ³×Æ®¿öÅ© Àü¼Û ÇÔ¼ö * @param fd ¼ÒÄÏ ÇÚµé * @param szBuf Àü¼Û ¹öÆÛ * @param iBufLen Àü¼Û ¹öÆÛ Å©±â * @return ¼º°øÇϸé Àü¼ÛÇÑ Å©±â¸¦ ¸®ÅÏÇÏ°í ½ÇÆÐÇϸé SOCKET_ERROR ¸¦ ¸®ÅÏÇÑ´Ù. */ int TcpSend( Socket fd, const char * szBuf, int iBufLen ) { int n; int iSendLen = 0; while( 1 ) { n = send( fd, szBuf + iSendLen, iBufLen - iSendLen, 0 ); if( n == SOCKET_ERROR ) return SOCKET_ERROR ; iSendLen += n; if( iSendLen == iBufLen ) break; } return iBufLen; } /** * @ingroup SipPlatform * @brief timeout À» °¡Áø TCP ¼ö½Å ¸Þ¼Òµå * @param fd ¼ÒÄÏ ÇÚµé * @param szBuf ¼ö½Å ¹öÆÛ * @param iBufLen ¼ö½Å ¹öÆÛ Å©±â * @param iSecond ¼ö½Å timeout ( ÃÊ ´ÜÀ§ ) * @returns ¼º°øÇÏ¸é ¼ö½Å ¹öÆÛ Å©±â¸¦ ¸®ÅÏÇÏ°í ½ÇÆÐÇϸé SOCKET_ERROR À» ¸®ÅÏÇÑ´Ù. */ int TcpRecv( Socket fd, char * szBuf, int iBufLen, int iSecond ) { int n; pollfd sttPoll[1]; TcpSetPollIn( sttPoll[0], fd ); n = poll( sttPoll, 1, 1000 * iSecond ); if( n <= 0 ) { return SOCKET_ERROR; } return recv( fd, szBuf, iBufLen, 0 ); } /** * @ingroup SipPlatform * @brief ¼ö½Å ¹öÆÛ°¡ °¡µæ Âû ¶§±îÁö µ¥ÀÌÅ͸¦ ¼ö½ÅÇÑ´Ù. * @param fd ¼ÒÄÏ ÇÚµé * @param szBuf ¼ö½Å ¹öÆÛ * @param iBufLen ¼ö½Å ¹öÆÛ Å©±â * @param iSecond ¼ö½Å timeout ( ÃÊ ´ÜÀ§ ) * @returns ¼º°øÇÏ¸é ¼ö½Å ¹öÆÛ Å©±â¸¦ ¸®ÅÏÇÏ°í ½ÇÆÐÇϸé SOCKET_ERROR À» ¸®ÅÏÇÑ´Ù. */ int TcpRecvSize( Socket fd, char * szBuf, int iBufLen, int iSecond ) { int n, iRecvLen = 0; pollfd sttPoll[1]; TcpSetPollIn( sttPoll[0], fd ); while( iRecvLen < iBufLen ) { n = poll( sttPoll, 1, 1000 * iSecond ); if( n <= 0 ) { return SOCKET_ERROR; } n = recv( fd, szBuf + iRecvLen, iBufLen - iRecvLen, 0 ); if( n <= 0 ) return SOCKET_ERROR; iRecvLen += n; } return iRecvLen; } /** * @ingroup SipPlatform * @brief TCP ¼­¹ö ¼ÒÄÏÀ» »ý¼ºÇÑ´Ù. * @param iPort TCP Æ÷Æ® ¹øÈ£ * @param iListenQ queue number to listen * @param pszIp ¼ö½Å IP ÁÖ¼Ò * @param bIpv6 IPv6 Àΰ¡? * @return ¼º°øÇÏ¸é ¼ÒÄÏ ÇÚµéÀ» ¸®ÅÏÇÏ°í ½ÇÆÐÇϸé INVALID_SOCKET ¸¦ ¸®ÅÏÇÑ´Ù. */ Socket TcpListen( int iPort, int iListenQ, const char * pszIp, bool bIpv6 ) { Socket fd; const int on = 1; #ifndef WINXP if( bIpv6 ) { struct sockaddr_in6 addr; // create socket. if( ( fd = socket( AF_INET6, SOCK_STREAM, 0 )) == INVALID_SOCKET ) { return INVALID_SOCKET; } memset( &addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_port = htons(iPort); if( pszIp ) { inet_pton( AF_INET6, pszIp, &addr.sin6_addr ); } else { addr.sin6_addr = in6addr_any; } if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) == -1 ) { } if( bind( fd, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR ) { closesocket( fd ); return INVALID_SOCKET; } } else #endif { struct sockaddr_in addr; // create socket. if( ( fd = socket( AF_INET, SOCK_STREAM, 0 )) == INVALID_SOCKET ) { return INVALID_SOCKET; } memset( &addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(iPort); if( pszIp ) { #ifdef WINXP addr.sin_addr.s_addr = inet_addr(pszIp); #else inet_pton( AF_INET, pszIp, &addr.sin_addr.s_addr ); #endif } else { addr.sin_addr.s_addr = INADDR_ANY; } if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) == -1 ) { } if( bind( fd, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR ) { closesocket( fd ); return INVALID_SOCKET; } } if( listen( fd, iListenQ ) == SOCKET_ERROR ) { closesocket( fd ); return INVALID_SOCKET; } return fd; } /** * @ingroup SipPlatform * @brief TCP accept wrapper function * @param hListenFd TCP ¼­¹ö ¼ÒÄÏ * @param pszIp ¿¬°áµÈ Ŭ¶óÀÌ¾ðÆ® IP ÁÖ¼Ò°¡ ÀúÀåµÉ º¯¼ö * @param iIpSize pszIp º¯¼öÀÇ Å©±â * @param piPort ¿¬°áµÈ Ŭ¶óÀÌ¾ðÆ® Æ÷Æ®°¡ ÀúÀåµÉ º¯¼ö * @param bIpv6 IPv6 Àΰ¡? * @returns ¼º°øÇÏ¸é ¿¬°áµÈ Ŭ¶óÀÌ¾ðÆ® ¼ÒÄÏ ÇÚµéÀ» ¸®ÅÏÇÑ´Ù. * ½ÇÆÐÇϸé INVALID_SOCKET ¸¦ ¸®ÅÏÇÑ´Ù. */ Socket TcpAccept( Socket hListenFd, char * pszIp, int iIpSize, int * piPort, bool bIpv6 ) { socklen_t iAddrLen; Socket hConnFd; #ifndef WINXP if( bIpv6 ) { struct sockaddr_in6 sttAddr; iAddrLen = sizeof(sttAddr); hConnFd = accept( hListenFd, (struct sockaddr *)&sttAddr, &iAddrLen ); if( hConnFd != INVALID_SOCKET ) { if( piPort ) *piPort = ntohs( sttAddr.sin6_port ); if( pszIp && iIpSize > 0 ) { inet_ntop( AF_INET6, &sttAddr.sin6_addr, pszIp, iIpSize ); } } } else #endif { struct sockaddr_in sttAddr; iAddrLen = sizeof(sttAddr); hConnFd = accept( hListenFd, (struct sockaddr *)&sttAddr, &iAddrLen ); if( hConnFd != INVALID_SOCKET ) { if( piPort ) *piPort = ntohs( sttAddr.sin_port ); if( pszIp && iIpSize > 0 ) { #ifdef WINXP snprintf( pszIp, iIpSize, "%s", inet_ntoa( sttAddr.sin_addr ) ); #else inet_ntop( AF_INET, &sttAddr.sin_addr, pszIp, iIpSize ); #endif } } } return hConnFd; } /** * @ingroup SipPlatform * @brief ¼ÒÄÏÀÇ ·ÎÄà IP ÁÖ¼Ò¿Í Æ÷Æ® ¹øÈ£¸¦ ¸®ÅÏÇÑ´Ù. * @param hSocket ¼ÒÄÏ * @param strIp ·ÎÄà IP ÁÖ¼Ò ÀúÀå º¯¼ö * @param iPort ·ÎÄà Æ÷Æ® ¹øÈ£ ÀúÀå º¯¼ö * @returns ¼º°øÇϸé true ¸¦ ¸®ÅÏÇÏ°í ½ÇÆÐÇϸé false ¸¦ ¸®ÅÏÇÑ´Ù. */ bool GetLocalIpPort( Socket hSocket, std::string & strIp, int & iPort ) { if( hSocket == INVALID_SOCKET ) return false; struct sockaddr_in sttAddr; int iAddrSize = sizeof(sttAddr); char szIp[INET6_ADDRSTRLEN]; if( getsockname( hSocket, (struct sockaddr *)&sttAddr, (socklen_t*)&iAddrSize ) == SOCKET_ERROR ) return false; #ifdef WINXP snprintf( szIp, sizeof(szIp), "%s", inet_ntoa( sttAddr.sin_addr ) ); #else inet_ntop( AF_INET, &sttAddr.sin_addr, szIp, sizeof(szIp) ); #endif strIp = szIp; iPort = ntohs( sttAddr.sin_port ); return true; } #ifdef WIN32 /** make listening socket for TCP server. * - port reuse option À» »ç¿ëÇÏÁö ¾Ê´Â´Ù. * - À©µµ¿ì¿ë pipe ¸¦ À§Çؼ­ ¸¸µé¾îÁ³´Ù. * * @author Yee Young Han * @param iPort port number to listen * @param iListenQ queue number to listen * @return if success, return socket. * if can not create socket, return INVALID_SOCKET. */ static Socket TcpListenNotReuse( int iPort, int iListenQ, const char * pszIp ) { Socket fd; struct sockaddr_in addr; // create socket. if( ( fd = socket( AF_INET, SOCK_STREAM, 0 )) == INVALID_SOCKET ) { return INVALID_SOCKET; } memset( &addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(iPort); if( pszIp ) { #ifdef WINXP addr.sin_addr.s_addr = inet_addr(pszIp); #else inet_pton( AF_INET, pszIp, &addr.sin_addr.s_addr ); #endif } else { addr.sin_addr.s_addr = INADDR_ANY; } if( bind( fd, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR ) { closesocket( fd ); return INVALID_SOCKET; } if( listen( fd, iListenQ ) == SOCKET_ERROR ) { closesocket( fd ); return INVALID_SOCKET; } return fd; } static bool gbIsStartPipeThread = false; static int giListenPort = 0; /** À©µµ¿ì¿ë pipe ¸Þ¼Òµå¸¦ À§ÇÑ ¾²·¹µå */ THREAD_API PipeThread( LPVOID lpParameter ) { int iPort; char szBuf[12]; Socket hListenFd, hConnFd; for( iPort = 1024; iPort < 65535; ++iPort ) { hListenFd = TcpListenNotReuse( iPort, 255, "127.0.0.1" ); if( hListenFd != INVALID_SOCKET ) break; } if( hListenFd == INVALID_SOCKET ) return 0; giListenPort = iPort; while( 1 ) { hConnFd = accept( hListenFd, NULL, NULL ); if( hConnFd == INVALID_SOCKET ) break; memcpy( szBuf, &hConnFd, sizeof(hConnFd) ); send( hConnFd, szBuf, sizeof(hConnFd), 0 ); } return 0; } /** À©µµ¿ì¿ë pipe ¸Þ¼Òµå * * @param filedes pipe ¸¦ À§ÇÑ ¼ÒÄÏ ¹è¿­ * @return ¼º°øÇϸé 0 À» ¸®ÅÏÇÑ´Ù. ½ÇÆÐÇϸé -1 À» ¸®ÅÏÇÑ´Ù. */ int pipe( Socket filedes[2] ) { if( gbIsStartPipeThread == false ) { DWORD dwThreadId; HANDLE hThread; hThread = CreateThread( NULL, 0, PipeThread, NULL, 0, &dwThreadId ); if( hThread == NULL ) { return -1; } gbIsStartPipeThread = true; // PipeThread ¿¡¼­ ¼ÒÄÏÀ» »ý¼ºÇÒ ¶§±îÁö ´ë±âÇÑ´Ù. (1ÃÊ) for( int i = 0; i < 50; ++i ) { if( giListenPort != 0 ) break; Sleep( 20 ); } } int n; char szBuf[12]; if( giListenPort == 0 ) return -1; filedes[1] = TcpConnect( "127.0.0.1", giListenPort ); if( filedes[1] == INVALID_SOCKET ) return -1; n = recv( filedes[1], szBuf, sizeof(Socket), 0 ); if( n != sizeof(Socket) ) { closesocket( filedes[1] ); return -1; } memcpy( &filedes[0], szBuf, sizeof(Socket) ); return 0; } #endif
X Tutup