// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// conv.CPP
// Descrpition: This file implements the functions for local socket(AF_LOCAL, PF_LOCAL, AF_UNIX)
//
//
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <utf.h>
#include "fdesc.h"
#include "systemspecialfilercg.h"
#include "lposix.h"
// -----------------------------------------------------------------------------
// CFileSocketDesc::Socket
// Creates socket using RSocket::Open
// -----------------------------------------------------------------------------
//
TInt CFileSocketDesc::Socket(RSocketServ& aSs, int /*aFamily*/, int aStyle, int aProtocol)
{
if(aProtocol == 0)
{
switch (aStyle)
{
case SOCK_STREAM:
aProtocol = IPPROTO_TCP;
break;
case SOCK_DGRAM:
aProtocol = IPPROTO_UDP;
break;
default:
aProtocol = KUndefinedProtocol;
}
}
iFcntlFlag = 0;
iStyle = aStyle;
iProtocol = aProtocol;
TInt err = CreateLock();
if (err)
{
return err;
}
err = iSocket.Open(aSs, KAfInet, aStyle, aProtocol);
return err;
}
// -----------------------------------------------------------------------------
// CFileSocketDesc::Bind
// Binds the socket to path provided in the aAddr
// Creates a filesystem entry for the path, binds iSocket to a loopback address and
// registers the address with ipc server.
//
// -----------------------------------------------------------------------------
TInt CFileSocketDesc::Bind(const struct sockaddr* aAddr, unsigned long aSize)
{
struct SSpecialFileMagicHeader enBuf;
int err;
const wchar_t* wspath = NULL;
if(aSize == NULL)
{
return EINVAL;
}
sockaddr_un* addr = (sockaddr_un*)aAddr;
if((err=ValidateAddress(addr,&aSize)) != KErrNone)
{
return err;
}
if (iPath.Length() != 0)
{
//Already bound condition
return EINVAL;
}
err = _EncodeSystemSplFileMagicHeader(&enBuf, EFileTypeSocket);
if (err != 0)
{
return err;
}
TPtrC8 sunpath((TUint8 *)addr->sun_path);
TFileName fname16;
err = CnvUtfConverter::ConvertToUnicodeFromUtf8(fname16, sunpath);
if(err != KErrNone)
{
return err;
}
wspath = (const wchar_t*)(fname16.PtrZ());
TInt errorNum;
err = _CreateSysSplFile(wspath, (char*)&enBuf, sizeof(struct SSpecialFileMagicHeader), errorNum, Backend()->FileSession());
if (err != KErrNone)
{
if (errorNum == EEXIST)
{
err = EADDRINUSE;
}
return err;
}
TFileName fullpathName;
err=GetFullFile(fullpathName,(const TText16*)wspath,Backend()->FileSession());
if(err != KErrNone)
{
return err;
}
err = CnvUtfConverter::ConvertFromUnicodeToUtf8(iPath, fullpathName);
if(err != KErrNone)
{
return err;
}
TInetAddr inetAddr(KInetAddrLoop,0);
err = iSocket.Bind(inetAddr);
if(err != KErrNone)
{
iPath.Zero();
return err;
}
TUint portNum = iSocket.LocalPort();
if(KErrNone == err)
{
err = Backend()->iIpcS.RegisterSockAddrWithIPCSvr(iPath,portNum);
if(KErrNone != err)
{
iPath.Zero();
return err;
}
}
return KErrNone;
}
// -----------------------------------------------------------------------------
// CFileSocketDesc::Accept
// Accepts connections from clients
// -----------------------------------------------------------------------------
//
void CFileSocketDesc::Accept(CFileDescBase*& aNewSocket, TRequestStatus& aStatus, RSocketServ& aSs, TSockAddr *)
{
TAutoFastLock lock(iReadLock);
if (iStyle == SOCK_DGRAM) // Listen on UDP socket, crashing at RSocket::Listen().
{
Complete(aStatus,KErrNotSupported);
return;
}
//coverity[alloc_fn]
//coverity[assign]
CFileSocketDesc *newSocket = new CFileSocketDesc;
TInt err=KErrNone;
if (newSocket!=0)
{
err = newSocket->CreateLock();
if (err)
{
Complete(aStatus, KErrNoMemory);
delete newSocket;
aNewSocket = NULL;
//coverity[memory_leak]
return;
}
err=newSocket->iSocket.Open(aSs);
}
if (newSocket ==0 || err!=KErrNone)
{
Complete(aStatus,KErrNoMemory);
delete newSocket;
aNewSocket = NULL;
//coverity[memory_leak]
return;
}
newSocket->iStyle = iStyle;
newSocket->iProtocol = iProtocol;
iSocket.Accept(newSocket->iSocket,aStatus);
aNewSocket = newSocket;
}
// -----------------------------------------------------------------------------
// CFileSocketDesc::AcceptCancel
// Cancels the accept request
// -----------------------------------------------------------------------------
//
void CFileSocketDesc::AcceptCancel()
{
iSocket.CancelAccept();
}
// -----------------------------------------------------------------------------
// CFileSocketDesc::Connect
// Gets the local socket port number from the IPCs and connects to that port
// -----------------------------------------------------------------------------
//
void CFileSocketDesc::Connect(const struct sockaddr* addr, unsigned long size,TRequestStatus& aStatus)
{
TUint portNo,ret;
if((ret = GetLocalSockPortByPath((sockaddr_un*)addr,size,portNo)) != KErrNone)
{
aStatus = ret;
return;
}
TInetAddr sAddr(KInetAddrLoop,portNo);
iSocket.Connect(sAddr,aStatus);
User::WaitForRequest(aStatus);
}
// -----------------------------------------------------------------------------
// CFileSocketDesc::ConnectCancel
// Cancels the connect request
// -----------------------------------------------------------------------------
//
void CFileSocketDesc::ConnectCancel()
{
iSocket.CancelConnect();
}
// -----------------------------------------------------------------------------
// CFileSocketDesc::Listen
// Sets up the listen buffer
// -----------------------------------------------------------------------------
//
TInt CFileSocketDesc::Listen(TUint qSize)
{
if (iStyle == SOCK_DGRAM) // Listen on UDP socket, crashing at RSocket::Listen().
{
return EOPNOTSUPP;
}
return CSockDescBase::Listen(qSize);
}
// -----------------------------------------------------------------------------
// CFileSocketDesc::Write
//
// -----------------------------------------------------------------------------
void CFileSocketDesc::Write (TDes8& aBuf, TRequestStatus& aStatus)
{
//Acquire the Lock before write and release it later
TAutoFastLock lock(iWriteLock);
CSockDescBase::Write(aBuf,aStatus);
return;
}
// -----------------------------------------------------------------------------
// CFileSocketDesc::Read
//
// -----------------------------------------------------------------------------
void CFileSocketDesc::Read (TDes8& aBuf, TRequestStatus& aStatus)
{
//Acquire the Lock before read and release it later
TAutoFastLock lock(iReadLock);
CSockDescBase::Read(aBuf,aStatus);
return;
}
// -----------------------------------------------------------------------------
// CFileSocketDesc::RecvFrom
//
// -----------------------------------------------------------------------------
void CFileSocketDesc::RecvFrom(TDes8& aDesc, TSockAddr& AFrom, int aFlags, TRequestStatus& aStatus)
{
TAutoFastLock lock(iReadLock);
CSockDescBase::RecvFrom(aDesc, AFrom, aFlags, aStatus);
return;
}
// -----------------------------------------------------------------------------
// CFileSocketDesc::SendTo
//
// -----------------------------------------------------------------------------
void CFileSocketDesc::SendTo(TDes8& aDesc, const struct sockaddr* aToAddr, unsigned long aAddrLen,int aFlags, TRequestStatus& aStatus)
{
TAutoFastLock lock(iWriteLock);
if(iStyle == SOCK_DGRAM)
{
TUint portNo;
TInt ret;
if((ret = GetLocalSockPortByPath((sockaddr_un*)aToAddr,aAddrLen,portNo)) != KErrNone)
{
Complete(aStatus,ret);
return;
}
TInetAddr sAddr(KInetAddrLoop,portNo);
CSockDescBase::SendTo(aDesc, sAddr, aFlags, aStatus);
return;
}
TUSockAddr addr(NULL,0);
CSockDescBase::SendTo(aDesc, addr, aFlags, aStatus);
return;
}
// -----------------------------------------------------------------------------
// CFileSocketDesc::Ioctl
// Not supported as of now.
// -----------------------------------------------------------------------------
void CFileSocketDesc::Ioctl(int /*aCmd*/, void* /*aParam*/, TRequestStatus& aStatus)
{
Complete(aStatus, KErrNotSupported);
return;
}
// -----------------------------------------------------------------------------
// CFileSocketDesc::FinalClose
//
// -----------------------------------------------------------------------------
TInt CFileSocketDesc::FinalClose()
{
RemoveLocalSockAddr();
RHeap* oheap = User::SwitchHeap(Backend()->Heap());
CSockDescBase::FinalClose();
User::SwitchHeap(oheap);
return KErrNone;
}
// -----------------------------------------------------------------------------
// CFileSocketDesc::GetSockOpt
// Get the socket option
// -----------------------------------------------------------------------------
TInt CFileSocketDesc::GetSockOpt(TInt anOptionName,TInt anOptionLevel,TDes8& anOption)
{
if (anOption.Ptr() == NULL)
{
return EFAULT;
}
if (anOption.Length() == 0)
{
return EINVAL;
}
if(anOptionLevel != SOL_SOCKET)
{
return KErrNotSupported;
}
switch(anOptionName)
{
case SO_TYPE:
TInt size;
size = (anOption.MaxLength() < sizeof(iStyle))? anOption.MaxLength(): sizeof(iStyle);
Mem::Copy((unsigned char*)anOption.Ptr(), &iStyle, size);
anOption.SetLength(size);
return KErrNone;
case SO_KEEPALIVE:
anOptionLevel=SOL_TCP;
break;
case SO_SNDBUF:
case SO_RCVBUF:
break;
default:
return KErrNotSupported;
}
return iSocket.GetOpt(anOptionName,anOptionLevel,anOption);
}
// -----------------------------------------------------------------------------
// CFileSocketDesc::SetSockOpt
// Set the socket option
// -----------------------------------------------------------------------------
TInt CFileSocketDesc::SetSockOpt(TInt anOptionName,TInt anOptionLevel,TDesC8& anOption)
{
if (anOption.Ptr() == NULL)
{
return EFAULT;
}
if (anOption.Length() == 0)
{
return EINVAL;
}
if(anOptionLevel != SOL_SOCKET)
{
return KErrNotSupported;
}
switch(anOptionName)
{
case SO_KEEPALIVE:
if(iStyle == SOCK_STREAM)
{
anOptionLevel=SOL_TCP;
}
else
{
return KErrNotSupported;
}
break;
case SO_SNDBUF:
case SO_RCVBUF:
break;
default:
return KErrNotSupported;
}
return iSocket.SetOpt(anOptionName,anOptionLevel,anOption);
}
// ---------------------------------------------------------------------------------
// CFileSocketDesc::SockName
// Fetches the local socket address.
// ---------------------------------------------------------------------------------
//
TInt CFileSocketDesc::SockName(int anEnd, struct sockaddr* aAddr,unsigned long* aAddrLen)
{
TInt ret = KErrNone;
TUSockAddr lSockAddr;
if(anEnd == 0)
{
TInt ret;
if((ret = ValidateAddress((sockaddr_un*)aAddr,aAddrLen)) != KErrNone)
{
if ( ( ret == EINVAL ) && (aAddrLen != 0))
{
// since the length passed is less than the first field but greater than 0 i.e sun_family we dont need to fill any field into aAddr structure passed.
return KErrNone;
}
return ret;
}
struct sockaddr_un* addr = (struct sockaddr_un*)aAddr;
TInt buflen = *aAddrLen - (sizeof(addr->sun_family) + sizeof(addr->sun_len));
addr->sun_family = AF_UNIX;
*aAddrLen = sizeof(addr->sun_family);
if(iPath.Length() && buflen > 1)
{
TPtr8 sockPath((TText8*)(addr->sun_path),buflen);
sockPath.Copy(iPath.Left(buflen-1));
sockPath.ZeroTerminate();
*aAddrLen = sizeof(addr->sun_family) + sockPath.Length() + 1; //+1 for NULL character
}
else if (buflen == 1)
{
addr->sun_path[0] = '\0';
}
return KErrNone;
}
else
{
ret = CSockDescBase::SockName(anEnd,lSockAddr);
if( ret == EFAULT )
return ret;
struct sockaddr_un* addr = (struct sockaddr_un*)aAddr;
// If the length passed is greater than or equal to 0 but less than the first field i.e sun_family , return kErrnone and dont fill any field into aAddr structure passed.
if( (aAddrLen != 0) && (*aAddrLen < sizeof(addr->sun_family)) )
{
return KErrNone;
}
if(ret == KErrNone)
{
ret = GetLocalSockAddrByPort((sockaddr_un*)aAddr,aAddrLen,lSockAddr.Port());
}
}
return ret;
}
// ---------------------------------------------------------------------------------
// CFileSocketDesc::ValidateAddress
// Utility function to check the validity of the socket address.
// ---------------------------------------------------------------------------------
//
TInt CFileSocketDesc::ValidateAddress(const struct sockaddr_un* aAddr,unsigned long* aAddrLen)
{
if (aAddr == NULL)
{
return EFAULT;
}
if (aAddrLen == 0)
{
return EINVAL;
}
if( *aAddrLen < sizeof(aAddr->sun_family) )
{
return EINVAL;
}
return KErrNone;
}
// ---------------------------------------------------------------------------------
// CFileSocketDesc::GetLocalSockAddrByPort
// Gets the file socket address associated with the port by querying the IPC server.
// ---------------------------------------------------------------------------------
//
TInt CFileSocketDesc::GetLocalSockAddrByPort(struct sockaddr_un* aAddr,unsigned long* aAddrLen,TUint aPortNum)
{
TInt ret;
if((ret = ValidateAddress(aAddr,aAddrLen)) != KErrNone)
{
return ret;
}
TInt buflen = *aAddrLen - (sizeof(aAddr->sun_family) + sizeof(aAddr->sun_len));
TInt err;
aAddr->sun_family = AF_UNIX;
*aAddrLen = sizeof(aAddr->sun_family);
if(buflen > 1)
{
TPtr8 sockPath((TText8*)aAddr->sun_path,buflen-1);
if((err=Backend()->iIpcS.GetLocalSockAddrByPort(sockPath,aPortNum)) != KErrNone)
{
sockPath.ZeroTerminate();
*aAddrLen = 0;
return err;
}
aAddr->sun_path[sockPath.Length()] = '\0';
*aAddrLen = sizeof(aAddr->sun_family) + sockPath.Length() + 1; //+1 for NULL character
return KErrNone;
}
else if (buflen == 1)
{
aAddr->sun_path[0] = '\0';
}
return KErrNone;
}
// -----------------------------------------------------------------------------
// CFileSocketDesc::GetLocalSockPortByPath
// Queries the IPC server to get the port associated with the path
// -----------------------------------------------------------------------------
TInt CFileSocketDesc::GetLocalSockPortByPath(const struct sockaddr_un* aAddr,unsigned long aAddrLen,TUint& aPortNum)
{
TInt err;
if((err = ValidateAddress(aAddr,&aAddrLen)) != KErrNone)
{
return err;
}
TInt buflen = aAddrLen - (sizeof(aAddr->sun_family) + sizeof(aAddr->sun_len));
if(buflen <= 1)
{
return EINVAL;
}
TBuf8<KMaxFileName> fullname;
err = GetFullFileUtf8(fullname, aAddr->sun_path, Backend()->FileSession());
return Backend()->iIpcS.GetLocalSockPortByPath(fullname, aPortNum);
}
// -----------------------------------------------------------------------------
// CFileSocketDesc::RemoveLocalSockAddr
// Removes the local socket address from the IPCServer
// -----------------------------------------------------------------------------
TInt CFileSocketDesc::RemoveLocalSockAddr()
{
if(iPath.Length())
{
return Backend()->iIpcS.RemLocalSockAddr(iPath);
}
return KErrNone;
}