#include "protoPipe.h"
#include "protoDebug.h"
#ifdef WIN32
#include <winreg.h>
#include <tchar.h>
#else
#include <unistd.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <stdlib.h>
#ifdef NO_SCM_RIGHTS
#undef SCM_RIGHTS
#endif #define CLI_PERM S_IRWXU
#endif
ProtoPipe::ProtoPipe(Type theType)
: ProtoSocket((MESSAGE == theType) ? UDP : TCP),
#ifdef WIN32
named_event_handle(INVALID_HANDLE_VALUE)
#else
unlink_tried(false)
#endif {
domain = LOCAL;
path[0] = '\0';
}
ProtoPipe::~ProtoPipe()
{
Close();
}
#ifdef WIN32
void ProtoPipe::Close()
{
if (IsOpen())
{
ProtoSocket::Close();
if ('\0' != path[0])
{
#ifdef _UNICODE
wchar_t wideBuffer[PATH_MAX];
mbstowcs(wideBuffer, path, strlen(path)+1);
LPCTSTR namePtr = wideBuffer;
#else
LPCSTR namePtr = path;
#endif HKEY hKey;
if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
_T("Software\\Protokit"),
0, KEY_SET_VALUE, &hKey))
{
if (ERROR_SUCCESS != RegDeleteValue(hKey, namePtr))
PLOG(PL_ERROR, "ProtoPipe::Close() RegDeleteValue() error: %s\n", ::GetErrorString());
RegCloseKey(hKey);
}
else
{
PLOG(PL_ERROR, "ProtoPipe::Close() RegOpenKeyEx() error: %s\n", ::GetErrorString());
}
}
if (INVALID_HANDLE_VALUE != named_event_handle)
{
CloseHandle(named_event_handle);
named_event_handle = INVALID_HANDLE_VALUE;
}
} }
bool ProtoPipe::Listen(const char* theName)
{
if (NULL != named_event_handle) CloseHandle(named_event_handle);
char pipeName[MAX_PATH];
strcpy(pipeName, "Global\\protoPipe-");
strncat(pipeName, theName, MAX_PATH - strlen(pipeName));
#ifdef _UNICODE
wchar_t wideBuffer[MAX_PATH];
mbstowcs(wideBuffer, pipeName, strlen(pipeName)+1);
LPCTSTR namePtr = wideBuffer;
#else
LPCTSTR namePtr = pipeName;
#endif named_event_handle = CreateEvent(NULL, TRUE, TRUE, namePtr);
if (NULL == named_event_handle)
{
named_event_handle = INVALID_HANDLE_VALUE;
PLOG(PL_ERROR, "ProtoPipe::Listen() CreateEvent() error: %s\n", ::GetErrorString());
return false;
}
else if (ERROR_ALREADY_EXISTS == GetLastError())
{
PLOG(PL_ERROR, "ProtoPipe::Listen() error: pipe already exists\n");
Close();
return false;
}
if (ProtoSocket::Listen())
{
HKEY hKey;
DWORD dwAction;
long theError = 0;
if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_LOCAL_MACHINE,
_T("Software\\Protokit"),
0L,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey,
&dwAction))
{
#ifdef _UNICODE
mbstowcs(wideBuffer, theName, strlen(theName)+1);
namePtr = wideBuffer;
#else
namePtr = theName;
#endif DWORD thePort = (DWORD)ProtoSocket::GetPort();
if (ERROR_SUCCESS != RegSetValueEx(hKey, namePtr, 0L, REG_DWORD,
(BYTE*)&thePort, sizeof(DWORD)))
{
PLOG(PL_ERROR, "ProtoPipe::Listen() RegSetValueEx() error: %s\n", ::GetErrorString());
RegCloseKey(hKey);
Close();
return false;
}
RegCloseKey(hKey);
}
else
{
PLOG(PL_ERROR, "ProtoPipe::Listen() RegCreateKeyEx() error: %s (Must be run as administrator)\n", ::GetErrorString());
Close();
return false;
}
}
else
{
PLOG(PL_ERROR, "ProtoPipe::Listen() error listening to socket\n");
Close();
return false;
}
strncpy(path, theName, PATH_MAX);
return true;
}
bool ProtoPipe::Accept(ProtoPipe* thePipe)
{
return ProtoSocket::Accept(static_cast<ProtoSocket*>(thePipe));
}
bool ProtoPipe::Connect(const char* theName)
{
if (INVALID_HANDLE_VALUE != named_event_handle) CloseHandle(named_event_handle);
char pipeName[MAX_PATH];
strcpy(pipeName, "Global\\protoPipe-");
strncat(pipeName, theName, MAX_PATH - strlen(pipeName));
#ifdef _UNICODE
wchar_t wideBuffer[MAX_PATH];
mbstowcs(wideBuffer, pipeName, strlen(pipeName)+1);
LPCTSTR namePtr = wideBuffer;
#else
LPCTSTR namePtr = pipeName;
#endif
named_event_handle = OpenEvent(EVENT_ALL_ACCESS, FALSE, namePtr);
if (NULL != named_event_handle)
{
CloseHandle(named_event_handle);
named_event_handle = INVALID_HANDLE_VALUE;
#ifdef _UNICODE
mbstowcs(wideBuffer, theName, strlen(theName)+1);
namePtr = wideBuffer;
#else
namePtr = theName;
#endif
DWORD thePort = 0;
HKEY hKey;
if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
_T("Software\\Protokit"),
0, KEY_READ, &hKey))
{
DWORD dwType;
DWORD dwLen = sizeof(DWORD);
if (ERROR_SUCCESS == RegQueryValueEx(hKey, namePtr, NULL, &dwType,
(BYTE*)&thePort, &dwLen))
{
if (REG_DWORD != dwType)
{
PLOG(PL_ERROR, "ProtoPipe::Connect() registry entry type mismatch!\n");
RegCloseKey(hKey);
Close();
return false;
}
}
else
{
PLOG(PL_ERROR, "ProtoPipe::Connect() RegQueryValueEx() error: %s\n", ::GetErrorString());
RegCloseKey(hKey);
Close();
return false;
}
RegCloseKey(hKey);
}
else
{
PLOG(PL_ERROR, "ProtoPipe::Connect() RegOpenKeyEx() error: %s\n", ::GetErrorString());
Close();
return false;
}
if ((thePort < 1) || (thePort > 0x0ffff))
{
PLOG(PL_ERROR, "ProtoPipe::Connect() error: bad port value!?\n");
Close();
return false;
}
ProtoAddress pipeAddr;
pipeAddr.ResolveFromString("127.0.0.1");
pipeAddr.SetPort((UINT16)thePort);
if (!ProtoSocket::Connect(pipeAddr))
{
PLOG(PL_ERROR, "ProtoPipe::Connect() error connecting socket!\n");
Close();
return false;
}
}
else
{
PLOG(PL_ERROR, "ProtoPipe::Connect() error (pipe not found): %s\n", ::GetErrorString());
return false;
}
return true;
}
#else
bool ProtoPipe::Open(const char* theName)
{
if (IsOpen()) Close();
char pipeName[PATH_MAX] = {0};
if(*theName!='/')
{
#ifdef __ANDROID__
strcpy(pipeName, "/data/local/tmp/");
#else
strcpy(pipeName, "/tmp/");
#endif }
strncat(pipeName, theName, PATH_MAX-strlen(pipeName));
struct sockaddr_un sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr));
sockAddr.sun_family = AF_UNIX;
strcpy(sockAddr.sun_path, pipeName);
#ifdef SCM_RIGHTS
size_t len = sizeof(sockAddr.sun_len) + sizeof(sockAddr.sun_family) +
strlen(sockAddr.sun_path) + 1;
#else
size_t len = strlen(sockAddr.sun_path) + sizeof(sockAddr.sun_family);
#endif int socketType = (UDP == protocol) ? SOCK_DGRAM : SOCK_STREAM;
if ((handle = socket(AF_UNIX, socketType, 0)) < 0)
{
PLOG(PL_ERROR, "ProtoPipe::Open() socket() error: %s\n", GetErrorString());
Close();
return false;
}
if (bind(handle, (struct sockaddr*)&sockAddr, (socklen_t)len) < 0)
{
PLOG(PL_WARN, "ProtoPipe::Open() bind(%s) error: %s\n", pipeName, GetErrorString());
Close();
return false;
}
state = IDLE;
port = 0;
if (!UpdateNotification())
{
PLOG(PL_ERROR, "ProtoPipe::Open() error updating notification\n");
Close();
return false;
}
strncpy(path, theName, PATH_MAX);
return true;
}
void ProtoPipe::Close()
{
if ('\0' != path[0])
{
Unlink(path);
path[0] = '\0';
}
ProtoSocket::Close();
}
void ProtoPipe::Unlink(const char* theName)
{
char pipeName[PATH_MAX] = {0};
if(*theName!='/')
{
#ifdef __ANDROID__
strcpy(pipeName, "/data/local/tmp/");
#else
strcpy(pipeName, "/tmp/");
#endif }
strncat(pipeName, theName, PATH_MAX - strlen(pipeName));
unlink(pipeName);
}
bool ProtoPipe::Listen(const char* theName)
{
if (IsOpen()) Close();
if (Open(theName))
{
if (TCP == protocol)
{
state = LISTENING;
if (!UpdateNotification())
{
PLOG(PL_ERROR, "ProtoSocket::Listen() error updating notification\n");
Close();
return false;
}
if (listen(handle, 5) < 0)
{
PLOG(PL_ERROR, "ProtoSocket:Listen() listen() error: %s\n", GetErrorString());
Close();
return false;
}
}
return true;
}
else
{
if (Connect(theName))
{
Close();
PLOG(PL_WARN, "ProtoPipe::Listen() error: name already in use\n");
return false;
}
else
{
#ifndef WIN32
if (unlink_tried)
{
unlink_tried = false;
}
else
{
Unlink(theName);
unlink_tried = true;
if (Listen(theName))
{
unlink_tried = false;
return true;
}
unlink_tried = false;
}
#endif
PLOG(PL_ERROR, "ProtoPipe::Listen() error opening pipe\n");
}
return false;
}
}
bool ProtoPipe::Accept(ProtoPipe* thePipe)
{
return ProtoSocket::Accept(static_cast<ProtoSocket*>(thePipe));
}
bool ProtoPipe::Connect(const char* theName)
{
if (!IsOpen())
{
char pipeName[PATH_MAX];
#ifdef __ANDROID__
strcpy(pipeName, "/data/local/tmp/protoSocketXXXXXX");
#else
strcpy(pipeName, "/tmp/protoSocketXXXXXX");
#endif int fd = mkstemp(pipeName);
if (fd < 0)
{
PLOG(PL_ERROR, "ProtoPipe::Connect() mkstemp() error: %s\n", GetErrorString());
return false;
}
else
{
close(fd);
unlink(pipeName);
}
if (!Open(pipeName+5))
{
PLOG(PL_ERROR, "ProtoPipe::Connect() error opening local domain socket\n");
return false;
}
if (TCP == protocol)
{
struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger = 5000;
if (setsockopt(handle, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger)) < 0)
PLOG(PL_ERROR, "ProtoPipe::Connect() setsockopt(SO_LINGER) error: %s\n", GetErrorString());
}
if (chmod(pipeName, CLI_PERM) < 0)
{
PLOG(PL_ERROR, "ProtoPipe::Connect(): chmod() error: %s\n", GetErrorString());
Close();
return false;
}
}
struct sockaddr_un serverAddr;
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sun_family = AF_UNIX;
if(*theName!='/')
{
#ifdef __ANDROID__
strcpy(serverAddr.sun_path, "/data/local/tmp/");
#else
strcpy(serverAddr.sun_path, "/tmp/");
#endif }
size_t pathMax = sizeof(serverAddr.sun_path);
strncat(serverAddr.sun_path, theName, pathMax - strlen(serverAddr.sun_path));
#ifdef SCM_RIGHTS
size_t addrLen = sizeof(serverAddr.sun_len) + sizeof(serverAddr.sun_family) +
strlen(serverAddr.sun_path) + 1;
#else
int addrLen = strlen(serverAddr.sun_path) + sizeof(serverAddr.sun_family);
#endif ProtoPipe::Notifier* savedNotifier = notifier;
if (NULL != savedNotifier) SetNotifier((ProtoPipe::Notifier*)NULL);
if (connect(handle, (struct sockaddr*)&serverAddr, (socklen_t)addrLen) < 0)
{
PLOG(PL_DEBUG, "ProtoPipe::Connect(): connect() error: %s\n", GetErrorString());
Close();
if (NULL != savedNotifier) SetNotifier(savedNotifier);
return false;
}
if (NULL != savedNotifier) SetNotifier(savedNotifier);
state = CONNECTED;
if (!UpdateNotification())
{
PLOG(PL_ERROR, "ProtoPipe::Connect() error updating notification\n");
Close();
return false;
}
return true;
}
#endif