version 1.2, 2015/04/20 18:10:35
|
version 1.3, 2015/04/20 18:20:34
|
|
|
#endif | #endif |
| |
#include <base/log.h> | #include <base/log.h> |
#include <base/strings.h> |
#include <pal/strings.h> |
#include <base/io.h> |
#include <pal/format.h> |
| |
#define T MI_T |
// #define ENABLE_TRACING 1 |
|
#ifdef ENABLE_TRACING |
|
# define TRACING_LEVEL 4 |
|
# include <deprecated/logging/logging.h> |
|
#else |
|
# define LOGE2(a) |
|
# define LOGW2(a) |
|
# define LOGD2(a) |
|
# define LOGX2(a) |
|
#endif |
|
|
|
#define MINSOCKBUFSIZE 4096 |
| |
/* | /* |
**============================================================================== | **============================================================================== |
|
|
typedef int socklen_t; | typedef int socklen_t; |
#endif | #endif |
| |
|
MI_INLINE int _GetErrorCode() |
|
{ |
|
#if defined(CONFIG_OS_WINDOWS) |
|
return WSAGetLastError(); |
|
#else |
|
return errno; |
|
#endif |
|
} |
|
|
MI_INLINE int _TestWOULDBLOCK() | MI_INLINE int _TestWOULDBLOCK() |
{ | { |
#if defined(CONFIG_OS_WINDOWS) | #if defined(CONFIG_OS_WINDOWS) |
return GetLastError() == WSAEWOULDBLOCK; |
return WSAGetLastError() == WSAEWOULDBLOCK; |
#else | #else |
return errno == EWOULDBLOCK || errno == EINPROGRESS; | return errno == EWOULDBLOCK || errno == EINPROGRESS; |
#endif | #endif |
|
|
MI_INLINE int _TestEINTR() | MI_INLINE int _TestEINTR() |
{ | { |
#if defined(CONFIG_OS_WINDOWS) | #if defined(CONFIG_OS_WINDOWS) |
return GetLastError() == WSAEINTR; |
return WSAGetLastError() == WSAEINTR; |
#else | #else |
return errno == EINTR; | return errno == EINTR; |
#endif | #endif |
} | } |
| |
|
MI_INLINE void _LogSockWriteError() |
|
{ |
|
#if defined(CONFIG_OS_WINDOWS) |
|
#else |
|
trace_SockWrite_Failed(errno); |
|
#endif |
|
} |
|
|
MI_INLINE int _Read(Sock sock, void* data, size_t size) | MI_INLINE int _Read(Sock sock, void* data, size_t size) |
{ | { |
#if defined(CONFIG_OS_WINDOWS) | #if defined(CONFIG_OS_WINDOWS) |
return recv(sock, data, (int)size, 0); | return recv(sock, data, (int)size, 0); |
#else | #else |
return read(sock, data, size); |
int n = read(sock, data, size); |
|
if (n < 0) |
|
{ |
|
if (errno == EAGAIN) |
|
LOGD2((ZT("_Read - read failed with EAGAIN. socket: %d"), sock)); |
|
else |
|
LOGE2((ZT("_Read - read failed. socket: %d, errno: %d (%s)"), sock, errno, strerror(errno))); |
|
} |
|
return n; |
#endif | #endif |
} | } |
| |
|
|
#if defined(CONFIG_OS_WINDOWS) | #if defined(CONFIG_OS_WINDOWS) |
return send(sock, data, (int)size, 0); | return send(sock, data, (int)size, 0); |
#else | #else |
return write(sock, data, size); |
int n = write(sock, data, size); |
|
if (n < 0) |
|
{ |
|
LOGE2((ZT("_Write - write failed. socket: %d, errno: %d (%s)"), sock, errno, strerror(errno))); |
|
} |
|
return n; |
#endif | #endif |
} | } |
| |
|
|
#endif /* CONFIG_OS_WINDOWS */ | #endif /* CONFIG_OS_WINDOWS */ |
} | } |
| |
MI_Result Sock_Create(Sock* sock) |
MI_Result Sock_Create( |
|
Sock* sock, |
|
MI_Boolean is_ipv6) |
{ | { |
*sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
*sock = socket(is_ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, IPPROTO_TCP); |
|
if (*sock == INVALID_SOCK) |
if (*sock == -1) |
|
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
| |
if (MI_RESULT_OK != Sock_SetCloseOnExec(*sock,MI_TRUE)) | if (MI_RESULT_OK != Sock_SetCloseOnExec(*sock,MI_TRUE)) |
{ |
trace_fcntl_failed(errno); |
LOGW((T("fcntl(F_SETFD) failed"))); |
|
} |
|
| |
return MI_RESULT_OK; | return MI_RESULT_OK; |
} | } |
|
|
#else | #else |
status = close(self); | status = close(self); |
#endif | #endif |
|
trace_Sock_Close((int)self); |
return status == 0 ? MI_RESULT_OK : MI_RESULT_FAILED; | return status == 0 ? MI_RESULT_OK : MI_RESULT_FAILED; |
} | } |
| |
MI_Result Sock_Bind(Sock self, const Addr* addr) |
MI_Result Sock_Bind( |
|
Sock self, |
|
const Addr* addr) |
{ | { |
int r; | int r; |
| |
r = bind(self, (struct sockaddr*)addr, sizeof(*addr)); |
r = bind(self, &addr->u.sock_addr, (socklen_t)addr->sock_addr_size); |
|
|
if (r != 0) | if (r != 0) |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
| |
|
|
{ | { |
if (_TestWOULDBLOCK() || _TestEAGAIN()) | if (_TestWOULDBLOCK() || _TestEAGAIN()) |
return MI_RESULT_WOULD_BLOCK; | return MI_RESULT_WOULD_BLOCK; |
|
else |
|
return MI_RESULT_FAILED; |
} | } |
| |
if (MI_RESULT_OK != Sock_SetCloseOnExec(*sock,MI_TRUE)) | if (MI_RESULT_OK != Sock_SetCloseOnExec(*sock,MI_TRUE)) |
{ | { |
LOGW((T("fcntl(F_SETFD) failed"))); |
trace_fcntl_failed(errno); |
} | } |
|
trace_Socket_Accept((int)*sock); |
return MI_RESULT_OK; | return MI_RESULT_OK; |
} | } |
| |
|
|
return MI_RESULT_OK; | return MI_RESULT_OK; |
} | } |
| |
|
|
MI_Result Sock_Connect( | MI_Result Sock_Connect( |
Sock self, | Sock self, |
const Addr* addr) | const Addr* addr) |
{ | { |
int r; | int r; |
|
int flags; |
|
int isNonBlockingSocket = 0; |
|
int connectErrno; |
|
|
|
LOGD2((ZT("Sock_Connect - Begin. socket: %d"), self)); |
|
// lockMutex(self); |
|
flags = fcntl(self, F_GETFL, 0); |
|
if ((flags & O_NONBLOCK) != 0) |
|
{ |
|
isNonBlockingSocket = 1; |
|
flags &= ~O_NONBLOCK; |
|
fcntl(self, F_SETFL, flags); |
|
} |
| |
r = connect(self, (struct sockaddr*)addr, sizeof(*addr)); |
r = connect(self, &addr->u.sock_addr, (size_t)addr->sock_addr_size); |
|
connectErrno = errno; |
| |
if (r != 0) |
if (isNonBlockingSocket != 0) |
{ | { |
if (_TestWOULDBLOCK() || _TestEAGAIN()) |
flags |= O_NONBLOCK; |
return MI_RESULT_WOULD_BLOCK; |
fcntl(self, F_SETFL, flags); |
|
} |
| |
|
// unlockMutex(self); |
|
if (r < 0) |
|
{ |
|
LOGE2((ZT("Sock_Connect - Error from connect. socket: %d: errno: %d (%s)"), self, connectErrno, strerror(connectErrno))); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
| |
|
LOGD2((ZT("Sock_Connect - OK exit. socket: %d"), self)); |
return MI_RESULT_OK; | return MI_RESULT_OK; |
} | } |
| |
|
|
{ | { |
int n; | int n; |
| |
|
LOGD2((ZT("Sock_Read - Begin. socket: %d, size: %u"), (int)self, (unsigned int)size)); |
|
|
* sizeRead = 0; | * sizeRead = 0; |
| |
for (;;) | for (;;) |
|
|
if (n >= 0) | if (n >= 0) |
{ | { |
*sizeRead = n; | *sizeRead = n; |
|
LOGD2((ZT("Sock_Read - OK exit. %d bytes read"), n)); |
return MI_RESULT_OK; | return MI_RESULT_OK; |
} | } |
| |
/* Repeat operation if instruction interrupted */ | /* Repeat operation if instruction interrupted */ |
if (_TestEINTR()) | if (_TestEINTR()) |
|
{ |
|
LOGD2((ZT("Sock_Read - _Read returned EINTR"))); |
continue; | continue; |
|
} |
| |
/* Check for would block error */ | /* Check for would block error */ |
if (_TestWOULDBLOCK() || _TestEAGAIN()) | if (_TestWOULDBLOCK() || _TestEAGAIN()) |
|
{ |
|
LOGD2((ZT("Sock_Read - _Read returned WOULD_BLOCK"))); |
return MI_RESULT_WOULD_BLOCK; | return MI_RESULT_WOULD_BLOCK; |
|
} |
| |
/* Failed */ | /* Failed */ |
|
trace_SockRead_Failed( _GetErrorCode() ); |
break; | break; |
}; |
} |
|
|
|
LOGE2((ZT("Sock_Read - _Read failed with errno: %d (%s)"), errno, strerror(errno))); |
| |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
|
|
{ | { |
int n; | int n; |
| |
|
LOGD2((ZT("Sock_Write - Begin. socket: %d, size: %u"), (int)self, (unsigned int)size)); |
|
|
*sizeWritten = 0; | *sizeWritten = 0; |
| |
for (;;) | for (;;) |
{ | { |
/* Attempt to read bytes */ |
/* Attempt to write bytes */ |
n = _Write(self, data, size); | n = _Write(self, data, size); |
| |
if (n >= 0) | if (n >= 0) |
{ | { |
*sizeWritten = n; |
*sizeWritten = (size_t)n; |
|
LOGD2((ZT("Sock_Write - OK exit. %d bytes written"), n)); |
return MI_RESULT_OK; | return MI_RESULT_OK; |
} | } |
| |
/* Repeat operation if instruction interrupted */ | /* Repeat operation if instruction interrupted */ |
if (_TestEINTR()) | if (_TestEINTR()) |
|
{ |
|
LOGD2((ZT("Sock_Write - _Write returned EINTR"))); |
continue; | continue; |
|
} |
| |
/* Check for would block error */ | /* Check for would block error */ |
if (_TestWOULDBLOCK() || _TestEAGAIN()) | if (_TestWOULDBLOCK() || _TestEAGAIN()) |
|
{ |
|
LOGD2((ZT("Sock_Write - _Write returned WOULD_BLOCK"))); |
return MI_RESULT_WOULD_BLOCK; | return MI_RESULT_WOULD_BLOCK; |
|
} |
| |
/* Failed */ | /* Failed */ |
|
if (size > MINSOCKBUFSIZE) |
|
{ |
|
/* Retry operation with smaller buffer size */ |
|
size = size >> 1; |
|
} |
|
else |
|
{ |
|
_LogSockWriteError(); |
break; | break; |
}; |
} |
|
} |
| |
|
LOGE2((ZT("Sock_Write - _Write failed with errno:L %d (%s)"), errno, strerror(errno))); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
| |
|
|
return MI_RESULT_WOULD_BLOCK; | return MI_RESULT_WOULD_BLOCK; |
| |
/* Failed */ | /* Failed */ |
|
trace_SockReadV_Failed( _GetErrorCode() ); |
break; | break; |
}; |
} |
| |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
|
|
size_t iovcnt, | size_t iovcnt, |
size_t* sizeWritten) | size_t* sizeWritten) |
{ | { |
int n; |
int n, error; |
| |
* sizeWritten = 0; | * sizeWritten = 0; |
| |
|
|
return MI_RESULT_WOULD_BLOCK; | return MI_RESULT_WOULD_BLOCK; |
| |
/* Failed */ | /* Failed */ |
|
error = _GetErrorCode(); |
|
trace_SockWriteV_Failed(error); |
|
#ifdef CONFIG_OS_WINDOWS |
|
// On Windows initial send auth message can fail because the sockets is not connected yet |
|
// in that case we send this special error so it can be retried |
|
if (WSAENOTCONN == error) |
|
{ |
|
return MI_RESULT_NOT_FOUND; |
|
} |
|
#endif |
break; | break; |
}; |
} |
| |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
|
|
| |
/* Create socket */ | /* Create socket */ |
{ | { |
r = Sock_Create(sock); |
r = Sock_Create(sock, addr->is_ipv6); |
| |
if (r != MI_RESULT_OK) | if (r != MI_RESULT_OK) |
return r; | return r; |
|
|
MI_Result r; | MI_Result r; |
struct sockaddr_un addr; | struct sockaddr_un addr; |
| |
|
|
*sock = socket(PF_UNIX, SOCK_STREAM, 0); | *sock = socket(PF_UNIX, SOCK_STREAM, 0); |
| |
if (*sock == -1) | if (*sock == -1) |
|
|
| |
if (MI_RESULT_OK != Sock_SetCloseOnExec(*sock,MI_TRUE)) | if (MI_RESULT_OK != Sock_SetCloseOnExec(*sock,MI_TRUE)) |
{ | { |
LOGW((T("fcntl(F_SETFD) failed"))); |
trace_fcntl_failed(errno); |
} | } |
| |
/* Reuse the address (to prevent binding failures) */ | /* Reuse the address (to prevent binding failures) */ |
|
|
>= sizeof(addr.sun_path)) | >= sizeof(addr.sun_path)) |
{ | { |
Sock_Close(*sock); | Sock_Close(*sock); |
LOGE((T("socket file path too long (> %u): '%s'"), |
trace_SocketFilePathTooLong( |
(int)(sizeof(addr.sun_path)-1), socketName)); |
(int)(sizeof(addr.sun_path)-1), scs(socketName)); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
| |
unlink(socketName); | unlink(socketName); |
| |
if (0 != bind(*sock, (struct sockaddr *) &addr, sizeof(addr))) |
// if (bind(*sock, (const struct sockaddr_un*)&addr, (socklen_t)sizeof (struct sockaddr_un)) != 0) |
|
if (bind(*sock, (const struct sockaddr*)&addr, (socklen_t)sizeof (struct sockaddr_un)) != 0) |
{ | { |
Sock_Close(*sock); | Sock_Close(*sock); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
|
|
MI_Result r; | MI_Result r; |
| |
// Initialize address. | // Initialize address. |
r = Addr_Init(&addr, "127.0.0.1", port); |
r = Addr_Init(&addr, "127.0.0.1", port, MI_FALSE); |
if (r != MI_RESULT_OK) | if (r != MI_RESULT_OK) |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
| |
// Create client socket. | // Create client socket. |
r = Sock_Create(sock); |
r = Sock_Create(sock, MI_FALSE); |
if (r != MI_RESULT_OK) | if (r != MI_RESULT_OK) |
{ | { |
Sock_Close(*sock); | Sock_Close(*sock); |
|
|
*sock = socket(PF_UNIX, SOCK_STREAM, 0); | *sock = socket(PF_UNIX, SOCK_STREAM, 0); |
| |
if (*sock == -1) | if (*sock == -1) |
|
{ |
|
trace_LocalSocketFailed(socketName); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
|
} |
| |
if (MI_RESULT_OK != Sock_SetCloseOnExec(*sock,MI_TRUE)) | if (MI_RESULT_OK != Sock_SetCloseOnExec(*sock,MI_TRUE)) |
{ | { |
LOGW((T("fcntl(F_SETFD) failed"))); |
trace_LocalSocket_SetOnExecFailed(socketName); |
|
trace_fcntl_failed(errno); |
} | } |
| |
r = Sock_SetBlocking(*sock, MI_FALSE); | r = Sock_SetBlocking(*sock, MI_FALSE); |
if (r != MI_RESULT_OK) | if (r != MI_RESULT_OK) |
{ | { |
|
trace_LocalSocket_SetBlockingFailed(socketName); |
Sock_Close(*sock); | Sock_Close(*sock); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
|
|
memset(&addr, 0, sizeof(addr)); | memset(&addr, 0, sizeof(addr)); |
| |
addr.sun_family = AF_UNIX; | addr.sun_family = AF_UNIX; |
strncpy(addr.sun_path, socketName, sizeof(addr.sun_path)-1); |
strncpy(addr.sun_path, socketName, sizeof addr.sun_path - 1); |
| |
if (0 != connect(*sock, (struct sockaddr *) &addr, sizeof(addr))) |
// if (connect(*sock, (const struct sockaddr_un*)&addr, (socklen_t)sizeof (struct sockaddr_un)) != 0) |
|
if (connect(*sock, (const struct sockaddr*)&addr, (socklen_t)sizeof (struct sockaddr_un)) != 0) |
{ | { |
if (_TestWOULDBLOCK() || _TestEAGAIN()) | if (_TestWOULDBLOCK() || _TestEAGAIN()) |
return MI_RESULT_WOULD_BLOCK; | return MI_RESULT_WOULD_BLOCK; |
| |
|
trace_LocalSocket_ConnectFailed(socketName, errno); |
|
|
Sock_Close(*sock); | Sock_Close(*sock); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
|
|
return MI_RESULT_OK; | return MI_RESULT_OK; |
#endif | #endif |
} | } |
|
|
|
static MI_Result _CreateSocketAndConnect( |
|
Sock* s, |
|
Addr* addr) |
|
{ |
|
MI_Result r; |
|
|
|
// Create client socket. |
|
r = Sock_Create(s, addr->is_ipv6); |
|
if (r != MI_RESULT_OK) |
|
{ |
|
LOGE2((ZT("_CreateSocketAndConnect - Sock_Create failed. result: %d (%s)"), r, mistrerror(r))); |
|
return r; |
|
} |
|
|
|
r = Sock_SetBlocking(*s, MI_FALSE); |
|
if (r != MI_RESULT_OK) |
|
return r; |
|
|
|
r = Sock_Connect(*s, addr); |
|
if (r != MI_RESULT_OK) |
|
{ |
|
if (r == MI_RESULT_WOULD_BLOCK) |
|
{ |
|
LOGW2((ZT("_CreateSocketAndConnect - Sock_Connect would block"))); |
|
} |
|
else |
|
{ |
|
LOGE2((ZT("_CreateSocketAndConnect - Sock_Connect failed. result: %d (%s)"), r, mistrerror(r))); |
|
} |
|
} |
|
|
|
return r; |
|
} |
|
|
|
MI_Result Sock_CreateIPConnector( |
|
Sock* s, |
|
const char* hostAndPort) |
|
{ |
|
/* create a connector to a remote address given in the form _host_:_port_ */ |
|
Addr addr; |
|
MI_Result r; |
|
unsigned short port; |
|
size_t len; |
|
char host[128]; |
|
/* TODO: detect IPv6 numeric addresses, in the form XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX/dd */ |
|
const char* posColon = strchr(hostAndPort, ':'); |
|
|
|
if (posColon == NULL) |
|
return MI_RESULT_INVALID_PARAMETER; |
|
len = (size_t)(posColon - hostAndPort); |
|
if (len >= sizeof host) |
|
return MI_RESULT_INVALID_PARAMETER; |
|
port = (unsigned short)atol(posColon + 1); |
|
memcpy(host, hostAndPort, len); |
|
host[len] = '\0'; |
|
|
|
/* This code tries to connect using the primary addressing family */ |
|
/* (IPv4 or IPv6). If that fails and Addr_Init has a secondary */ |
|
/* addressing family, it tries using the secondary family next. */ |
|
|
|
/* Initialize primary family address. */ |
|
r = Addr_Init(&addr, host, port, MI_FALSE); |
|
if (r != MI_RESULT_OK) |
|
return r; |
|
|
|
/* Connect to server. */ |
|
r = _CreateSocketAndConnect(s, &addr); |
|
if (r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK) |
|
{ |
|
MI_Result r2; |
|
|
|
Sock_Close(*s); |
|
|
|
/* Initialize secondary address. */ |
|
r2 = Addr_Init(&addr, host, port, MI_TRUE); |
|
if (r2 != MI_RESULT_OK) |
|
return r; /* on error, return original failure */ |
|
r2 = _CreateSocketAndConnect(s, &addr); |
|
if (r2 != MI_RESULT_OK && r2 != MI_RESULT_WOULD_BLOCK) |
|
{ |
|
Sock_Close(*s); |
|
|
|
return r; /* on error, return original failure */ |
|
} |
|
r = r2; |
|
} |
|
|
|
#ifdef ENABLE_TRACING |
|
if (r != MI_RESULT_OK) |
|
LOGD2((ZT("_CreateConnector - Connect failure. host: %s, result: %d (%s)"), host, (int)r, mistrerror(r))); |
|
#endif |
|
|
|
return r; |
|
} |