/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 2006 Imendio AB
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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 <config.h>
//#include <glib.h>
//#include <glib/gi18n.h>
#ifndef G_OS_WIN32
//#include <libc/netinet/net_types.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <netinet/in.h>
//#include <libc/netinet/ip.h>
//#include <libc/netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
// hack - to compile without the error of n_long not defined
#define LM_SHUTDOWN SHUT_RDWR
#else /* G_OS_WIN32 */
#include <winsock2.h>
#define LM_SHUTDOWN SD_BOTH
#endif /* G_OS_WIN32 */
#include "lm-internals.h"
#include "lm-connection.h"
#include "lm-sock.h"
#include "lm-debug.h"
#define IPV6_MAX_ADDRESS_LEN 46 /* 45 + '\0' */
#ifdef EMULATOR
#include "libloudmouth_wsd_solution.h"
GET_GLOBAL_VAR_FROM_TLS(initialised,lm_sock,gboolean)
#define initialised (*GET_WSD_VAR_NAME(initialised,lm_sock,g)())
#else
static gboolean initialised = FALSE;
#endif
gboolean
_lm_sock_library_init (void)
{
#ifdef G_OS_WIN32
WORD version;
WSADATA data;
int error;
#endif /* G_OS_WIN32 */
if (initialised) {
return TRUE;
}
lm_verbose ("Socket library initialising...\n");
#ifdef G_OS_WIN32
lm_verbose ("Checking for winsock 2.0 or above...\n");
version = MAKEWORD (2, 0);
error = WSAStartup (version, &data);
if (error != 0) {
g_printerr ("WSAStartup() failed, error:%d\n", error);
return FALSE;
}
/* Confirm that the WinSock DLL supports 2.0.
* Note that if the DLL supports versions greater
* than 2.0 in addition to 2.0, it will still return
* 2.0 in wVersion since that is the version we
* requested.
*/
if (LOBYTE (data.wVersion) != 2 ||
HIBYTE (data.wVersion) != 0) {
/* Tell the user that we could not find a usable
* WinSock DLL.
*/
g_printerr ("Socket library version is not sufficient!\n");
WSACleanup ();
return FALSE;
}
#endif /* G_OS_WIN32 */
initialised = TRUE;
return TRUE;
}
void
_lm_sock_library_shutdown (void)
{
if (!initialised) {
return;
}
lm_verbose ("Socket library shutting down...\n");
#ifdef G_OS_WIN32
WSACleanup ();
#endif /* G_OS_WIN32 */
initialised = FALSE;
}
void
_lm_sock_set_blocking (LmSocketT sock,
gboolean block)
{
int res;
#ifndef G_OS_WIN32
res = fcntl (sock, F_SETFL, block ? 0 : O_NONBLOCK);
#else /* G_OS_WIN32 */
u_long mode = (block ? 0 : 1);
res = ioctlsocket (sock, FIONBIO, &mode);
#endif /* G_OS_WIN32 */
if (res != 0) {
// g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET,
// "Could not set connection to be %s\n",
// block ? "blocking" : "non-blocking");
lm_verbose ("[_lm_sock_set_blocking]: Could not set connection to be %s\n",
block ? "blocking" : "non-blocking");
}
}
void
_lm_sock_shutdown (LmSocketT sock)
{
shutdown (sock, LM_SHUTDOWN);
}
void
_lm_sock_close (LmSocketT sock)
{
#ifndef G_OS_WIN32
close (sock);
#else /* G_OS_WIN32 */
closesocket (sock);
#endif /* G_OS_WIN32 */
}
LmSocketT
_lm_sock_makesocket (int af,
int type,
int protocol)
{
int optval = 1;
int sock_fd = -1;
unsigned int optlen = sizeof(optval);
sock_fd = socket (af, type, protocol);
setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR|SO_REUSEPORT,&optval,optlen);
return (LmSocketT)sock_fd;
}
int
_lm_sock_connect (LmSocketT sock,
const struct sockaddr *name,
int namelen)
{
return connect (sock, name, namelen);
}
gboolean
_lm_sock_is_blocking_error (int err)
{
#ifndef G_OS_WIN32
return (err == _LM_SOCK_EINPROGRESS);
#else /* G_OS_WIN32 */
return (err == _LM_SOCK_EINPROGRESS ||
err == _LM_SOCK_EWOULDBLOCK ||
err == _LM_SOCK_EINVAL);
#endif /* G_OS_WIN32 */
}
gboolean
_lm_sock_is_blocking_success (int err)
{
//Bug fixed by MRT from week 50 devlong onwards
return (err == _LM_SOCK_EEXISTS);
//Error EEXIST = 17 is not expected here. But It should be mrt error 107
//fix take later
//Before week 50 SDK
//return (err == _LM_SOCK_EALREADY || err == _LM_SOCK_EISCONN);
}
int
_lm_sock_get_last_error (void)
{
#ifndef G_OS_WIN32
return errno;
#else /* G_OS_WIN32 */
return WSAGetLastError ();
#endif /* G_OS_WIN32 */
}
void
_lm_sock_get_error (LmSocketT sock,
void *error,
socklen_t *len)
{
getsockopt (sock, SOL_SOCKET, SO_ERROR, (void*) error, len);
}
const gchar *
_lm_sock_get_error_str (int err)
{
#ifndef G_OS_WIN32
return strerror (err);
#else /* G_OS_WIN32 */
switch (err) {
case WSAEINTR: return _("Connect interrupted and canceled");
case WSAEACCES: return _("Permission denied");
case WSAEFAULT: return _("Bad address");
case WSAEINVAL: return _("Invalid argument");
case WSAEMFILE: return _("Too many open sockets");
case WSAEWOULDBLOCK: return _("Resource temporarily unavailable");
case WSAEINPROGRESS: return _("Operation now in progress");
case WSAEALREADY: return _("Operation already in progress");
case WSAENOTSOCK: return _("Socket operation on nonsocket");
case WSAEDESTADDRREQ: return _("Destination address required");
case WSAEMSGSIZE: return _("Message too long");
case WSAEPROTOTYPE: return _("Protocol wrong type for socket");
case WSAENOPROTOOPT: return _("Bad protocol option");
case WSAEPROTONOSUPPORT: return _("Protocol not supported");
case WSAESOCKTNOSUPPORT: return _("Socket type not supported");
case WSAEOPNOTSUPP: return _("Operation not supported");
case WSAEPFNOSUPPORT: return _("Protocol family not supported");
case WSAEAFNOSUPPORT: return _("Address family not supported by protocol family");
case WSAEADDRINUSE: return _("Address already in use");
case WSAEADDRNOTAVAIL: return _("Can not assign requested address");
case WSAENETDOWN: return _("Network is down");
case WSAENETUNREACH: return _("Network is unreachable");
case WSAENETRESET: return _("Network dropped connection on reset");
case WSAECONNABORTED: return _("Software caused connection abort");
case WSAECONNRESET: return _("Connection reset by peer");
case WSAENOBUFS: return _("No buffer space available");
case WSAEISCONN: return _("Socket is already connected");
case WSAENOTCONN: return _("Socket is not connected");
case WSAESHUTDOWN: return _("Can not send after socket shutdown");
case WSAETIMEDOUT: return _("Connection timed out");
case WSAECONNREFUSED: return _("Connection refused");
case WSAEHOSTDOWN: return _("Host is down");
case WSAEHOSTUNREACH: return _("No route to host");
case WSAEPROCLIM: return _("Too many processes");
case WSASYSNOTREADY: return _("Network subsystem is unavailable");
case WSAVERNOTSUPPORTED: return _("Winsock library version is out of range ");
case WSANOTINITIALISED: return _("Successful WSAStartup not yet performed");
case WSAEDISCON: return _("Graceful shutdown in progress");
case WSATYPE_NOT_FOUND: return _("Class type not found");
case WSAHOST_NOT_FOUND: return _("Host not found");
case WSATRY_AGAIN: return _("Nonauthoritative host not found");
case WSANO_RECOVERY: return _("This is a nonrecoverable error");
case WSANO_DATA: return _("Valid name, no data record of requested type");
case WSA_INVALID_HANDLE: return _("Specified event object handle is invalid");
case WSA_INVALID_PARAMETER: return _("One or more parameters are invalid");
case WSA_IO_INCOMPLETE: return _("Overlapped I/O event object no in signaled state");
case WSA_IO_PENDING: return _("Overlapped operations will complete later");
case WSA_NOT_ENOUGH_MEMORY: return _("Insufficient memory available");
case WSA_OPERATION_ABORTED: return _("Overlapped operation aborted");
/* os dependent */
case WSASYSCALLFAILURE: return _("System call failure");
}
return _("Unknown");
#endif /* G_OS_WIN32 */
}
const gchar *
_lm_sock_addrinfo_get_error_str (int err)
{
switch (err) {
case EAI_AGAIN:
return ("The nameserver failed to return an "
"address, try again later");
case EAI_BADFLAGS:
return ("Internal error trying to obtain remote address");
case EAI_FAIL:
return ("The nameserver encountered errors "
"looking up this address");
/* EAI_NODATA is apparently missing on FreeBSD. On recent GNU libc,
* it requires _GNU_SOURCE to be defined; in the unlikely case that
* that GNU libc returns this value we'll return the default message */
#ifdef EAI_NODATA
case EAI_NODATA:
return _("The remote host exists but no address "
"is available");
#endif
case EAI_NONAME:
return ("The remote address is unknown");
case EAI_FAMILY:
case EAI_SERVICE:
case EAI_SOCKTYPE:
return ("The remote address is not obtainable "
"for that socket type.");
default:
break;
}
return ("The remote address could not be obtained ");
}
#ifdef USE_TCP_KEEPALIVES
gboolean
_lm_sock_set_keepalive (LmSocketT sock, int delay)
{
int opt;
lm_verbose ("[_lm_sock_set_keepalive]: USE_TCP_KEEPALIVES Enabled\n");
opt = 1;
if (setsockopt (sock, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof (opt)) < 0) {
return FALSE;
}
//opt = 3; /* 3 keepalives before considering connection dead */
/*if (setsockopt (sock, IPPROTO_TCP, TCP_KEEPCNT, &opt, sizeof (opt)) < 0) {
return FALSE;
}
opt = delay;
if (setsockopt (sock, IPPROTO_TCP, TCP_KEEPIDLE, &opt, sizeof (opt)) < 0) {
return FALSE;
}
opt = delay;
if (setsockopt (sock, IPPROTO_TCP, TCP_KEEPINTVL, &opt, sizeof (opt)) < 0) {
return FALSE;
}*/
UNUSED_FORMAL_PARAM(delay);
return TRUE;
}
#endif /* USE_TCP_KEEPALIVES */
gchar *
_lm_sock_get_local_host (LmSocketT sock)
{
struct sockaddr addr_info;
void *sock_addr;
socklen_t namelen;
char addrbuf[IPV6_MAX_ADDRESS_LEN];
const char *host;
namelen = sizeof (struct sockaddr);
if (getsockname (sock, &addr_info, &namelen)) {
return NULL;
}
switch (addr_info.sa_family) {
case AF_INET:
sock_addr = & (((struct sockaddr_in *) &addr_info)->sin_addr);
break;
case AF_INET6:
sock_addr = & (((struct sockaddr_in6 *) &addr_info)->sin6_addr);
break;
default:
return NULL;
}
/* inet_ntoa has been obsoleted in favour of inet_ntop */
host = inet_ntop (addr_info.sa_family,
sock_addr,
addrbuf,
IPV6_MAX_ADDRESS_LEN);
return g_strdup (host);
}