/*
belle-sip - SIP (RFC3261) library.
Copyright (C) 2010 Belledonne Communications SARL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "belle_sip_internal.h"
#ifdef _WIN32
#include
#include
#ifdef HAVE_COMPILER_TLS
static __declspec(thread) const void *current_thread_data = NULL;
#endif
static int sockets_initd=0;
int belle_sip_init_sockets(void){
if (sockets_initd==0){
WSADATA data;
int err = WSAStartup(MAKEWORD(2,2), &data);
if (err != 0) {
belle_sip_error("WSAStartup failed with error: %d\n", err);
return -1;
}
}
sockets_initd++;
return 0;
}
void belle_sip_uninit_sockets(void){
sockets_initd--;
if (sockets_initd==0) WSACleanup();
}
typedef struct thread_param {
void * (*func)(void *);
void * arg;
} thread_param_t;
static unsigned WINAPI thread_starter(void *data) {
thread_param_t *params = (thread_param_t*)data;
void *ret = params->func(params->arg);
belle_sip_free(data);
return (DWORD)ret;
}
int belle_sip_thread_create(belle_sip_thread_t *thread, void *attr, void * (*func)(void *), void *data) {
thread_param_t *params = belle_sip_new(thread_param_t);
params->func = func;
params->arg = data;
*thread = (HANDLE)_beginthreadex(NULL, 0, thread_starter, params, 0, NULL);
return 0;
}
int belle_sip_thread_join(belle_sip_thread_t thread, void **unused) {
if (thread != NULL) {
WaitForSingleObjectEx(thread, INFINITE, FALSE);
CloseHandle(thread);
}
return 0;
}
int belle_sip_mutex_init(belle_sip_mutex_t *mutex, void *attr) {
#ifdef BELLE_SIP_WINDOWS_DESKTOP
*mutex = CreateMutex(NULL, FALSE, NULL);
#else
InitializeSRWLock(mutex);
#endif
return 0;
}
int belle_sip_mutex_lock(belle_sip_mutex_t * hMutex) {
#ifdef BELLE_SIP_WINDOWS_DESKTOP
WaitForSingleObject(*hMutex, INFINITE);
#else
AcquireSRWLockExclusive(hMutex);
#endif
return 0;
}
int belle_sip_mutex_unlock(belle_sip_mutex_t * hMutex) {
#ifdef BELLE_SIP_WINDOWS_DESKTOP
ReleaseMutex(*hMutex);
#else
ReleaseSRWLockExclusive(hMutex);
#endif
return 0;
}
int belle_sip_mutex_destroy(belle_sip_mutex_t * hMutex) {
#ifdef BELLE_SIP_WINDOWS_DESKTOP
CloseHandle(*hMutex);
#endif
return 0;
}
int belle_sip_socket_set_nonblocking(belle_sip_socket_t sock)
{
unsigned long nonBlock = 1;
return ioctlsocket(sock, FIONBIO , &nonBlock);
}
int belle_sip_socket_set_dscp(belle_sip_socket_t sock, int ai_family, int dscp){
belle_sip_warning("belle_sip_socket_set_dscp(): not implemented.");
return -1;
}
const char *belle_sip_get_socket_error_string(){
return belle_sip_get_socket_error_string_from_code(WSAGetLastError());
}
const char *belle_sip_get_socket_error_string_from_code(int code){
static CHAR msgBuf[256];
#ifdef _UNICODE
static WCHAR wMsgBuf[256];
int ret;
FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
code,
0, // Default language
(LPWSTR) &wMsgBuf,
sizeof(wMsgBuf),
NULL);
ret = wcstombs(msgBuf, wMsgBuf, sizeof(msgBuf));
if (ret == sizeof(msgBuf)) msgBuf[sizeof(msgBuf) - 1] = '\0';
#else
FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
code,
0, // Default language
(LPTSTR) &msgBuf,
sizeof(msgBuf),
NULL);
/*FIXME: should convert from TCHAR to UTF8 */
#endif
return (const char *)msgBuf;
}
int belle_sip_thread_key_create(belle_sip_thread_key_t *key, void (*destructor)(void*) ){
#ifdef HAVE_COMPILER_TLS
*key = (belle_sip_thread_key_t)¤t_thread_data;
#else
*key=TlsAlloc();
if (*key==TLS_OUT_OF_INDEXES){
belle_sip_error("TlsAlloc(): TLS_OUT_OF_INDEXES.");
return -1;
}
#endif
return 0;
}
int belle_sip_thread_setspecific(belle_sip_thread_key_t key,const void *value){
#ifdef HAVE_COMPILER_TLS
current_thread_data = value;
return 0;
#else
return TlsSetValue(key,(void*)value) ? 0 : -1;
#endif
}
const void* belle_sip_thread_getspecific(belle_sip_thread_key_t key){
#ifdef HAVE_COMPILER_TLS
return current_thread_data;
#else
return TlsGetValue(key);
#endif
}
int belle_sip_thread_key_delete(belle_sip_thread_key_t key){
#ifdef HAVE_COMPILER_TLS
current_thread_data = NULL;
return 0;
#else
return TlsFree(key) ? 0 : -1;
#endif
}
#ifndef BELLE_SIP_WINDOWS_DESKTOP
void belle_sip_sleep(unsigned int ms) {
HANDLE sleepEvent = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
if (!sleepEvent)
return;
WaitForSingleObjectEx(sleepEvent, ms, FALSE);
}
#endif
#else
#include
int belle_sip_init_sockets(){
signal(SIGPIPE,SIG_IGN);
return 0;
}
void belle_sip_uninit_sockets(){
}
int belle_sip_socket_set_nonblocking(belle_sip_socket_t sock){
return fcntl (sock, F_SETFL, fcntl(sock,F_GETFL) | O_NONBLOCK);
}
int belle_sip_socket_set_dscp(belle_sip_socket_t sock, int ai_family, int dscp){
int tos;
int proto;
int value_type;
int retval;
tos = (dscp << 2) & 0xFC;
switch (ai_family) {
case AF_INET:
proto=IPPROTO_IP;
value_type=IP_TOS;
break;
case AF_INET6:
proto=IPPROTO_IPV6;
#ifdef IPV6_TCLASS /*seems not defined by my libc*/
value_type=IPV6_TCLASS;
#else
value_type=IP_TOS;
#endif
break;
default:
belle_sip_error("Cannot set DSCP because socket family is unspecified.");
return -1;
}
retval = setsockopt(sock, proto, value_type, (const char*)&tos, sizeof(tos));
if (retval==-1)
belle_sip_error("Fail to set DSCP value on socket: %s",belle_sip_get_socket_error_string());
return retval;
}
#endif
int belle_sip_socket_enable_dual_stack(belle_sip_socket_t sock){
int value=0;
int err=setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&value, sizeof(value));
if (err==-1){
belle_sip_warning("belle_sip_socket_enable_dual_stack: setsockopt(IPV6_ONLY) failed: %s",belle_sip_get_socket_error_string());
}
return err;
}
#if defined(ANDROID) || defined(_WIN32)
/*
* SHAME !!! bionic's getaddrinfo does not implement the AI_V4MAPPED flag !
* It is declared in header file but rejected by the implementation.
* The code below is to emulate a _compliant_ getaddrinfo for android.
**/
/**
* SHAME AGAIN !!! Win32's implementation of getaddrinfo is bogus !
* it is not able to return an IPv6 addrinfo from an IPv4 address when AI_V4MAPPED is set !
**/
struct addrinfo *_belle_sip_alloc_addrinfo(int ai_family, int socktype, int proto){
struct addrinfo *ai=(struct addrinfo*)belle_sip_malloc0(sizeof(struct addrinfo));
ai->ai_family=ai_family;
ai->ai_socktype=socktype;
ai->ai_protocol=proto;
ai->ai_addrlen=AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
ai->ai_addr=(struct sockaddr *) belle_sip_malloc0(ai->ai_addrlen);
return ai;
}
struct addrinfo *convert_to_v4mapped(const struct addrinfo *ai){
struct addrinfo *res=NULL;
const struct addrinfo *it;
struct addrinfo *v4m=NULL;
struct addrinfo *last=NULL;
for (it=ai;it!=NULL;it=it->ai_next){
struct sockaddr_in6 *sin6;
struct sockaddr_in *sin;
v4m=_belle_sip_alloc_addrinfo(AF_INET6, it->ai_socktype, it->ai_protocol);
v4m->ai_flags|=AI_V4MAPPED;
sin6=(struct sockaddr_in6*)v4m->ai_addr;
sin=(struct sockaddr_in*)it->ai_addr;
sin6->sin6_family=AF_INET6;
((uint8_t*)&sin6->sin6_addr)[10]=0xff;
((uint8_t*)&sin6->sin6_addr)[11]=0xff;
memcpy(((uint8_t*)&sin6->sin6_addr)+12,&sin->sin_addr,4);
sin6->sin6_port=sin->sin_port;
if (last){
last->ai_next=v4m;
}else{
res=v4m;
}
last=v4m;
}
return res;
}
struct addrinfo *addrinfo_concat(struct addrinfo *a1, struct addrinfo *a2){
struct addrinfo *it;
struct addrinfo *last=NULL;
for (it=a1;it!=NULL;it=it->ai_next){
last=it;
}
if (last){
last->ai_next=a2;
return a1;
}else
return a2;
}
int belle_sip_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res){
if (hints && hints->ai_family!=AF_INET && hints->ai_flags & AI_V4MAPPED){
struct addrinfo *res6=NULL;
struct addrinfo *res4=NULL;
struct addrinfo lhints={0};
int err;
if (hints) memcpy(&lhints,hints,sizeof(lhints));
lhints.ai_flags &= ~(AI_ALL | AI_V4MAPPED); /*remove the unsupported flags*/
if (hints->ai_flags & AI_ALL){
lhints.ai_family=AF_INET6;
err=getaddrinfo(node, service, &lhints, &res6);
}
lhints.ai_family=AF_INET;
err=getaddrinfo(node, service, &lhints, &res4);
if (err==0){
struct addrinfo *v4m=convert_to_v4mapped(res4);
freeaddrinfo(res4);
res4=v4m;
}
*res=addrinfo_concat(res6,res4);
if (*res) err=0;
return err;
}
return getaddrinfo(node, service, hints, res);
}
void _belle_sip_freeaddrinfo(struct addrinfo *res){
struct addrinfo *it,*next_it;
for(it=res;it!=NULL;it=next_it){
next_it=it->ai_next;
belle_sip_free(it->ai_addr);
belle_sip_free(it);
}
}
void belle_sip_freeaddrinfo(struct addrinfo *res){
struct addrinfo *it,*previt=NULL;
struct addrinfo *allocated_by_belle_sip=NULL;
for(it=res;it!=NULL;it=it->ai_next){
if (it->ai_flags & AI_V4MAPPED){
allocated_by_belle_sip=it;
if (previt) previt->ai_next=NULL;
break;
}
previt=it;
}
if (res!=allocated_by_belle_sip) freeaddrinfo(res);
if (allocated_by_belle_sip) _belle_sip_freeaddrinfo(allocated_by_belle_sip);
}
#else
int belle_sip_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res){
return getaddrinfo(node, service, hints, res);
}
void belle_sip_freeaddrinfo(struct addrinfo *res){
freeaddrinfo(res);
}
#endif