unix_net.cpp
上传用户:dangjiwu
上传日期:2013-07-19
资源大小:42019k
文件大小:51k
- /* ***** BEGIN LICENSE BLOCK *****
- * Source last modified: $Id: unix_net.cpp,v 1.6.2.4 2004/07/09 01:46:41 hubbe Exp $
- *
- * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
- *
- * The contents of this file, and the files included with this file,
- * are subject to the current version of the RealNetworks Public
- * Source License (the "RPSL") available at
- * http://www.helixcommunity.org/content/rpsl unless you have licensed
- * the file under the current version of the RealNetworks Community
- * Source License (the "RCSL") available at
- * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
- * will apply. You may also obtain the license terms directly from
- * RealNetworks. You may not use this file except in compliance with
- * the RPSL or, if you have a valid RCSL with RealNetworks applicable
- * to this file, the RCSL. Please see the applicable RPSL or RCSL for
- * the rights, obligations and limitations governing use of the
- * contents of the file.
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License Version 2 or later (the
- * "GPL") in which case the provisions of the GPL are applicable
- * instead of those above. If you wish to allow use of your version of
- * this file only under the terms of the GPL, and not to allow others
- * to use your version of this file under the terms of either the RPSL
- * or RCSL, indicate your decision by deleting the provisions above
- * and replace them with the notice and other provisions required by
- * the GPL. If you do not delete the provisions above, a recipient may
- * use your version of this file under the terms of any one of the
- * RPSL, the RCSL or the GPL.
- *
- * This file is part of the Helix DNA Technology. RealNetworks is the
- * developer of the Original Code and owns the copyrights in the
- * portions it created.
- *
- * This file, and the files included with this file, is distributed
- * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
- * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
- * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
- * ENJOYMENT OR NON-INFRINGEMENT.
- *
- * Technology Compatibility Kit Test Suite(s) Location:
- * http://www.helixcommunity.org/content/tck
- *
- * Contributor(s):
- *
- * ***** END LICENSE BLOCK ***** */
- #if defined _LINUX && defined __GLIBC__ && 0
- #define _JAVA_GREENTHREADS
- #endif
- // Java with green threads needs you to use the internal entry points
- // for these system calls
- #ifdef _JAVA_GREENTHREADS
- #define READ ::__read
- #define CONNECT ::__connect
- #define RECVFROM ::__recvfrom
- #else
- #define READ ::read
- #define CONNECT ::connect
- #define RECVFROM ::recvfrom
- #endif
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <assert.h>
- #include "hxcom.h"
- #include "hxbuffer.h"
- #include "timebuff.h"
- #include "hxtick.h"
- #include "netbyte.h"
- #include "platform/unix/unix_net.h" // Our declaration
- #include "hxheap.h"
- #include "hxslist.h"
- #include "hxcom.h"
- #include "hxengin.h"
- #include "hxstrutl.h"
- #include <errno.h>
- #ifdef _IRIX
- #include <bstring.h> // used in FD_ZERO
- #endif
- // #include "netplay.h" // why?
- #ifdef _AIX
- #include <sys/select.h>
- #endif
- #include <sys/types.h> //for waitpid
- #include <sys/wait.h> //for waitpid
- #include <fcntl.h>
- #ifdef _UNIX_THREADS_SUPPORTED
- #include "hxthread.h"
- #endif
- int unix_net::InBlockingMode = 0;
- // This thing doesn't get initialized if this is linked into a shared library
- CHXSimpleList* unix_net::readers = NULL;
- // So I've done a silly thing and labelled it so.
- static BOOL silly_unix_hack_initialized = FALSE;
- const int HOSTSTRUCTSIZE = sizeof( hostent );
- unix_net::unix_net()
- {
- set_sock( INVALID_SOCKET );
- mLastError = HXR_OK;
- callRaConnect = 1;
- bReadyToWrite = 0;
- m_SocketState = CONN_CLOSED;
- //Async DNS vars.
- CurrentAddr = 0;
- m_DNSOnly = FALSE;
- m_anDNSPipe[0] = nInvalidPipe;
- m_anDNSPipe[1] = nInvalidPipe;
- m_nChildProcID = 0;
- m_szPipeIP[0] = ' ';
-
- read_pending = FALSE;
- m_lRefCount = 0;
- // Don't allocate this yet. Not all unix_net instances are actually
- // used to read, and if one isn't, allocating here wastes memory.
- m_pInBuffer = NULL;
- m_bReuseAddr = FALSE;
- m_bReusePort = FALSE;
- m_pAsyncHost = NULL;
-
- #ifdef _UNIX_THREADS_SUPPORTED
- m_pResolver = NULL;
- #endif
- }
- unix_net::~unix_net()
- {
- m_SocketState = CONN_CLOSING;
- if ((get_sock() != INVALID_SOCKET) )
- {
- ::close(get_sock());
- set_sock( INVALID_SOCKET );
- }
- m_SocketState = CONN_CLOSED;
- mConnectionOpen = 0;
-
- LISTPOSITION listpos = readers->Find(this);
- if(listpos)
- {
- readers->RemoveAt(listpos);
- }
- HX_VECTOR_DELETE(m_pInBuffer);
- //If the DNS forked proccess is still running lets
- //kill it here.
- //Ignore any returned error, what would we do anyway?
- CleanUpChildProc();
- HX_VECTOR_DELETE(m_pAsyncHost);
- #ifdef _UNIX_THREADS_SUPPORTED
- if( m_bThreadedDNS )
- {
- if(m_pResolver)
- m_pResolver->Exit(0);
- m_nResolved = 0;
- HX_DELETE( m_pResolver );
- }
- #endif
- }
- void unix_net::CleanUpChildProc()
- {
- //Close any open pipes as well.
- if( m_anDNSPipe[0] >= 0 )
- {
- ::close( m_anDNSPipe[0] );
- m_anDNSPipe[0] = nInvalidPipe;
- }
-
- if( m_anDNSPipe[1] >= 0 )
- {
- ::close( m_anDNSPipe[1] );
- m_anDNSPipe[1] = nInvalidPipe;
- }
-
- if( m_nChildProcID != 0 )
- {
- kill( m_nChildProcID, SIGKILL );
- m_nChildProcID = 0;
- }
- }
- unix_net * unix_net::new_socket(UINT16 type)
- {
- unix_net *c = NULL;
-
- if(!silly_unix_hack_initialized)
- {
- readers = new CHXSimpleList;
- silly_unix_hack_initialized = TRUE;
- }
-
- switch(type)
- {
- case HX_TCP_SOCKET:
- c = new unix_TCP;
- readers->AddTail(c);
- break;
-
- case HX_UDP_SOCKET:
- c = new unix_UDP;
- readers->AddTail(c);
- break;
- }
-
- return(c);
- }
- // init_drivers() should do any network driver initialization here
- // params is a pointer to a platform specfic defined struct that
- // contains an required initialization data
- HX_RESULT unix_net::init_drivers(void *params)
- {
- return(HXR_OK);
- }
- /* close_drivers() should close any network drivers used by the program
- NOTE: The program MUST not make any other calls to the network drivers
- until init_drivers() has been called */
- HX_RESULT unix_net::close_drivers(void *params)
- {
- return(HXR_OK);
- }
- HX_RESULT
- unix_net::get_host_name(char *name, int namelen)
- {
- if (::gethostname(name, namelen) == 0)
- {
- return HXR_OK;
- }
- else
- {
- return HXR_FAIL;
- }
- }
- HX_RESULT
- unix_net::get_host_by_name(char *name, REF(struct hostent*) pHostent)
- {
- if (pHostent = ::gethostbyname(name))
- {
- return HXR_OK;
- }
- else
- {
- return HXR_FAIL;
- }
- }
- HX_RESULT unix_net::host_to_ip_str( char *host, char *ip, UINT32 ulIPBufLen)
- {
- HX_RESULT theErr = HXR_OK;
- ULONG32 dwAddress;
- struct sockaddr_in rInAddress;
- struct hostent* pHostEntry;
- // Let's look for this in the cache first
- if (conn::is_cached( host, &dwAddress))
- {
- //Found it, copy the 32bit address into rInAddress
- //w/o calling memcpy()
- rInAddress.sin_addr.s_addr = dwAddress;
- }
- else
- {
- // Do DNS on the host name
- if (!(pHostEntry = gethostbyname( host )))
- {
- // Error
- theErr = HXR_DNR;
- }
- // Return w/o attempting any copies if there's an error
- if (theErr != HXR_OK)
- {
- goto FuncExit;
- }
-
- // copy the ip address into rInAddress w/o calling memcpy()
- struct in_addr** ppAddr = (struct in_addr**)(pHostEntry->h_addr_list);
- memcpy(&rInAddress.sin_addr, ppAddr[0], sizeof(struct in_addr)); /* Flawfinder: ignore */
-
- // add to the dns cache
- conn::add_to_cache(host, (ULONG32) rInAddress.sin_addr.s_addr );
- }
- // Convert the ULONG32 IP address into a string and copy it into ip
- SafeStrCpy( ip, inet_ntoa( rInAddress.sin_addr ) , ulIPBufLen);
- // Single exit point
- FuncExit:
- return( theErr );
- }
- ULONG32 unix_net::AddRef()
- {
- return InterlockedIncrement(&m_lRefCount);
- }
- ULONG32 unix_net::Release()
- {
- if (InterlockedDecrement(&m_lRefCount) > 0)
- {
- return m_lRefCount;
- }
- delete this;
- return 0;
- }
- /*
- * reuse_addr/reuse_port has to be called before a sock binds. however,
- * socket is not available until it binds as it is implemented. So, set a
- * flag here and do the actual setsockopt right before a sock binds.
- * Look in init_unix().
- */
- HX_RESULT
- unix_net::reuse_addr(BOOL enable)
- {
- m_bReuseAddr = enable;
- return HXR_OK;
- }
- HX_RESULT
- unix_net::reuse_port(BOOL enable)
- {
- m_bReusePort = enable;
- return HXR_OK;
- }
- HX_RESULT unix_net::init_unix(UINT16 type, UINT32 local_addr, UINT16 port,
- UINT16 blocking)
- {
- int s = INVALID_SOCKET;
- struct sockaddr_in addr;
- #ifdef _BEOS
- char mode = 1;
- #else
- int mode = 1;
- #endif
-
- mLastError = HXR_OK;
- #ifdef _BEOS
- s = socket(AF_INET, type, 0);
- #else
- s = socket(PF_INET, type, 0);
- #endif
- if (s < 0)
- {
- mLastError = HXR_NET_SOCKET_INVALID;
- return mLastError;
- }
-
- if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&m_bReuseAddr, sizeof(m_bReuseAddr)) < 0)
- {
- mLastError = HXR_NET_SOCKET_INVALID;
- goto sock_err;
- }
- #if defined SO_REUSEPORT
- if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (const char*)&m_bReusePort, sizeof(m_bReusePort)) < 0)
- {
- mLastError = HXR_NET_SOCKET_INVALID;
- goto sock_err;
- }
- #endif
-
- memset(&addr, 0, sizeof addr);
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = htonl(local_addr);
- addr.sin_port = htons(port);
-
- if (::bind(s, (sockaddr*)&addr, sizeof addr) < 0)
- {
- mLastError = HXR_NET_SOCKET_INVALID;
- goto sock_err;
- }
- #ifdef FIONBIO
- if (!blocking && ioctl(s, FIONBIO, &mode) < 0)
- #elif SO_NONBLOCK
- if (!blocking && setsockopt(s,SOL_SOCKET,SO_NONBLOCK,&mode,1)<0)
- #else
- if (!blocking && ::fcntl(get_sock(), F_SETFL, ::fcntl(get_sock(), F_GETFL, 0) | O_NONBLOCK) < 0)
- #endif
- {
- mLastError = HXR_NET_SOCKET_INVALID;
- goto sock_err;
- }
- DPRINTF(D_MSG,("unix_net::socket opened: %dn", s));
- m_SocketState = CONN_OPEN;
- set_sock( s );
- return mLastError;
-
- sock_err:
- ::close(s);
- m_SocketState = CONN_NO_CONN;
- return mLastError;
- }
- #if 0
- /* mcast_multiple_if test */
- /*
- * it returns a number of multicast enabled NICs with a default multicast
- * interface as the very first entry in the pIFList
- */
- UINT32
- unix_net::detectMcastIF(REF(BYTE**) pIFList)
- {
- struct ifconf ifc;
- int i;
- ifc.ifc_len = sizeof(buff);
- ifc.ifc_buf = buff;
- if (ioctl(skfd, SIOCGIFCONF, &ifc) >= 0) {
- {
- // XXXGo
- //printf("%sn", pc);
- printf("%un", ul);
- }
- else
- {
- printf("coudn't detect ifn");
- }
- //HX_VECTOR_DELETE(pc);
-
- }
- #endif
- HX_RESULT unix_net::connect(const char* host, UINT16 port, UINT16 blocking, ULONG32 ulPlatform)
- {
- DPRINTF(D_MSG,("unix_net::connect(): b: %dn", blocking));
-
- bReadyToWrite = 0;
-
- //Get a host at all?
- if(!host)
- {
- mLastError = HXR_DNR;
- return mLastError;
- }
- //Do we have a socket yet?
- if(get_sock() < 0)
- {
- mLastError = HXR_NET_SOCKET_INVALID;
- return mLastError;
- }
-
- if( blocking )
- {
- //Set our state.
- m_SocketState = CONN_DNS_INPROG;
-
- //check and see if we were passed a dot format IP address.
- memset(&m_sSockAddrIn, 0, sizeof(struct sockaddr_in));
- char* pTemp = (char*)strrchr(host, '.');
- if (pTemp && atoi(pTemp + 1))
- {
- m_sSockAddrIn.sin_addr.s_addr = inet_addr(host);
-
- if ((ULONG32)m_sSockAddrIn.sin_addr.s_addr == (ULONG32)-1)
- {
- mLastError = HXR_DNR;
- return mLastError;
- }
- //Set state to show we have the address ready to go.
- m_SocketState = CONN_CONNECT_INPROG;
- }
-
- //do a blocking gethostbyname() call.
- if( m_SocketState == CONN_DNS_INPROG )
- {
- struct hostent *h = gethostbyname(host);
- if (!h || !h->h_addr )
- {
- mLastError = HXR_DNR;
- DPRINTF(D_MSG,("unix_net::connect() HXR_INVALID_HOSTrn"));
- CB_ConnectionComplete(0);
- return HXR_DNR;
- }
-
- struct in_addr** ppAddr = (struct in_addr**)(h->h_addr_list);
- memcpy(&m_sSockAddrIn.sin_addr, ppAddr[0], sizeof(struct in_addr)); /* Flawfinder: ignore */
- if (m_pAsyncHost != host)
- {
- HX_VECTOR_DELETE(m_pAsyncHost);
- m_pAsyncHost = ::new_string(host);
- }
- m_AsyncPort = port;
- }
-
- m_sSockAddrIn.sin_family = AF_INET;
- m_sSockAddrIn.sin_port = htons(port);
- // this stores info about current addr
- CurrentAddr = m_sSockAddrIn.sin_addr.s_addr;
- if(CONNECT(get_sock(), (sockaddr*)&m_sSockAddrIn, sizeof(struct sockaddr_in)))
- {
- if(!blocking && (errno == EWOULDBLOCK || errno == EINPROGRESS))
- {
- mConnectionOpen = 1;
- nonblocking();
- CB_ConnectionComplete(1);
- return HXR_OK;
- }
-
- DPRINTF(D_MSG,("unix_net::connect() HXR_NET_CONNECTrn"));
- mLastError = HXR_NET_CONNECT;
- CB_ConnectionComplete(0);
- return HXR_NET_CONNECT;
- }
-
- mConnectionOpen = 1;
- nonblocking();
- m_SocketState = CONN_OPEN; // Rahul
- bReadyToWrite = 1;
- CB_ConnectionComplete(1);
- return HXR_OK;
- }//blocking
- return ConnectAsync( host, port );
- }
- #ifdef _UNIX_THREADS_SUPPORTED
- void *unix_net::_ResolveIt( void* pArg )
- {
- unix_net* pIt = (unix_net*)pArg;
- struct hostent *h = gethostbyname(pIt->m_pAsyncHost);
- if( (NULL!=h) && (NULL!=h->h_addr) )
- {
- //Got good IP, send it back in dot format.
- const u_char *src = (u_char*)h->h_addr_list[0];
- static const char fmt[] = "%u.%u.%u.%u";
- sprintf(pIt->m_szPipeIP, fmt, src[0], src[1], src[2], src[3]); /* Flawfinder: ignore */
- }
- pIt->m_nResolved = 1;
- return NULL;
- }
- #endif
- HX_RESULT unix_net::DoStartAsyncConn()
- {
- DPRINTF(D_MSG,("unix_net::DoStartAsyncConn()rn"));
- m_SocketState = CONN_DNS_INPROG;
-
- #ifdef _UNIX_THREADS_SUPPORTED
- //Make the thread if we haven't already.
- if( m_bThreadedDNS )
- {
- if( NULL == m_pResolver)
- {
- m_nResolved = 0;
- HXThread::MakeThread( m_pResolver );
- HX_ASSERT( m_pResolver );
- }
-
- //Set the state
- m_nResolved = 0;
- //Start the thread
- m_pResolver->CreateThread( _ResolveIt, (void*)this );
- //That is it!
- return( mLastError = HXR_WOULD_BLOCK );
- }
- #endif
- //
- // Fork here to start the DNS process. The process will
- // be monitored by calls from proccess_idle to the method
- // 'CheckOnDNS()'.
- //
- //Create pipe to communicate between the child and parent.
- if ( 0 != pipe(m_anDNSPipe) )
- {
- //Can't create pipe.
- m_anDNSPipe[0] = nInvalidPipe;
- m_anDNSPipe[1] = nInvalidPipe;
- mLastError = HXR_GENERAL_NONET;
- return mLastError;
- }
- if( 0 > (m_nChildProcID = fork()))
- {
- //Error trying to fork.
- //What should we do?
- mLastError = HXR_GENERAL_NONET;
- m_SocketState = CONN_DNS_FAILED;
- CB_DNSComplete( 0 );
- return HXR_GENERAL_NONET;
- }
-
- if( 0 == m_nChildProcID )
- {
- //This is the child proc....
- //Close the read end of the pipe.
- if ( 0 != ::close( m_anDNSPipe[0]) )
- {
- //close, error. Kill this child proc.
- //This proc just exits. Return codes will come
- //from the parent proc. Just write a NULL to
- //the pipe.
- ::write( m_anDNSPipe[1], "