/*
* 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