/*
eXosip - This is the eXtended osip library.
Copyright (C) 2002, 2003 Aymeric MOIZARD - jack@atosc.org
eXosip 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.
eXosip 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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef ENABLE_MPATROL
#include <mpatrol.h>
#endif
#include <osipparser2/osip_port.h>
#include "eXosip2.h"
#if defined(_WIN32_WCE)
#elif defined(WIN32)
#include <WinDNS.h>
#else
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#ifdef HAVE_ARPA_NAMESER_H
#include <arpa/nameser.h>
#endif
#ifdef HAVE_RESOLV8_COMPAT_H
#include <nameser8_compat.h>
#include <resolv8_compat.h>
#elif defined(HAVE_RESOLV_H) || defined(OpenBSD) || defined(FreeBSD) || defined(NetBSD)
#include <resolv.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#endif
extern eXosip_t eXosip;
extern int ipv6_enable;
#if defined(WIN32) || defined(_WIN32_WCE)
/* You need the Platform SDK to compile this. */
#include <Windows.h>
#include <Iphlpapi.h>
int
eXosip_dns_get_local_fqdn (char **servername, char **serverip,
char **netmask, unsigned int WIN32_interface)
{
unsigned int pos;
*servername = NULL; /* no name on win32? */
*serverip = NULL;
*netmask = NULL;
/* First, try to get the interface where we should listen */
{
DWORD size_of_iptable = 0;
PMIB_IPADDRTABLE ipt;
PMIB_IFROW ifrow;
if (GetIpAddrTable (NULL, &size_of_iptable, TRUE) == ERROR_INSUFFICIENT_BUFFER)
{
ifrow = (PMIB_IFROW) _alloca (sizeof (MIB_IFROW));
ipt = (PMIB_IPADDRTABLE) _alloca (size_of_iptable);
if (ifrow == NULL || ipt == NULL)
{
/* not very usefull to continue */
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO4, NULL,
"ERROR alloca failed\r\n"));
return -1;
}
if (!GetIpAddrTable (ipt, &size_of_iptable, TRUE))
{
/* look for the best public interface */
for (pos = 0; pos < ipt->dwNumEntries && *netmask == NULL; ++pos)
{
/* index is */
struct in_addr addr;
struct in_addr mask;
ifrow->dwIndex = ipt->table[pos].dwIndex;
if (GetIfEntry (ifrow) == NO_ERROR)
{
switch (ifrow->dwType)
{
case MIB_IF_TYPE_LOOPBACK:
/* break; */
case MIB_IF_TYPE_ETHERNET:
default:
addr.s_addr = ipt->table[pos].dwAddr;
mask.s_addr = ipt->table[pos].dwMask;
if (ipt->table[pos].dwIndex == WIN32_interface)
{
*servername = NULL; /* no name on win32? */
*serverip = osip_strdup (inet_ntoa (addr));
*netmask = osip_strdup (inet_ntoa (mask));
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO4, NULL,
"Interface ethernet: %s/%s\r\n",
*serverip, *netmask));
break;
}
}
}
}
}
}
}
if (*serverip == NULL || *netmask == NULL)
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_WARNING, NULL,
"ERROR No network interface found\r\n"));
return -1;
}
return 0;
}
#if 1
int
eXosip_guess_ip_for_via (int family, char *address, int size)
{
SOCKET sock;
SOCKADDR_STORAGE local_addr;
int local_addr_len;
struct addrinfo *addrf;
address[0] = '\0';
sock = socket(family, SOCK_DGRAM, 0);
if(family == AF_INET)
{
getaddrinfo(eXosip.ipv4_for_gateway,NULL,NULL,&addrf);
}
else if(family == AF_INET6)
{
getaddrinfo("2001:238:202::1",NULL,NULL,&addrf);
}
if (addrf==NULL)
{
closesocket(sock);
snprintf(address, size, (family == AF_INET) ? "127.0.0.1" : "::1" );
return -1;
}
if(WSAIoctl(sock,SIO_ROUTING_INTERFACE_QUERY, addrf->ai_addr, addrf->ai_addrlen,
&local_addr, sizeof(local_addr), &local_addr_len, NULL, NULL) != 0)
{
closesocket(sock);
freeaddrinfo(addrf);
snprintf(address, size, (family == AF_INET) ? "127.0.0.1" : "::1" );
return -1;
}
closesocket(sock);
freeaddrinfo(addrf);
if(getnameinfo((const struct sockaddr*)&local_addr,
local_addr_len,address, size, NULL, 0, NI_NUMERICHOST))
{
snprintf(address, size, (family == AF_INET) ? "127.0.0.1" : "::1" );
return -1;
}
return 0;
}
#else
int
eXosip_guess_ip_for_via (int family, char *address, int size)
{
/* w2000 and W95/98 */
unsigned long best_interface_index;
DWORD hr;
/* NT4 (sp4 only?) */
PMIB_IPFORWARDTABLE ipfwdt;
DWORD siz_ipfwd_table = 0;
unsigned int ipf_cnt;
address[0] = '\0';
best_interface_index = -1;
/* w2000 and W95/98 only */
hr = GetBestInterface (inet_addr (eXosip.ipv4_for_gateway), &best_interface_index);
if (hr)
{
LPVOID lpMsgBuf;
FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
hr,
MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR) & lpMsgBuf, 0, NULL);
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO4, NULL,
"GetBestInterface: %s\r\n", lpMsgBuf));
best_interface_index = -1;
}
if (best_interface_index != -1)
{ /* probably W2000 or W95/W98 */
char *servername;
char *serverip;
char *netmask;
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO4, NULL,
"Default Interface found %i\r\n",
best_interface_index));
if (0 == eXosip_dns_get_local_fqdn (&servername, &serverip, &netmask,
best_interface_index))
{
osip_strncpy (address, serverip, size - 1);
osip_free (servername);
osip_free (serverip);
osip_free (netmask);
return 0;
}
return -1;
}
if (!GetIpForwardTable (NULL, &siz_ipfwd_table, FALSE) ==
ERROR_INSUFFICIENT_BUFFER
|| !(ipfwdt = (PMIB_IPFORWARDTABLE) alloca (siz_ipfwd_table)))
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO4, NULL,
"Allocation error\r\n"));
return -1;
}
/* NT4 (sp4 support only?) */
if (!GetIpForwardTable (ipfwdt, &siz_ipfwd_table, FALSE))
{
for (ipf_cnt = 0; ipf_cnt < ipfwdt->dwNumEntries; ++ipf_cnt)
{
if (ipfwdt->table[ipf_cnt].dwForwardDest == 0)
{ /* default gateway found */
char *servername;
char *serverip;
char *netmask;
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO4, NULL,
"Default Interface found %i\r\n",
ipfwdt->table[ipf_cnt].dwForwardIfIndex));
if (0 == eXosip_dns_get_local_fqdn (&servername,
&serverip,
&netmask,
ipfwdt->table[ipf_cnt].
dwForwardIfIndex))
{
osip_strncpy (address, serverip, size);
osip_free (servername);
osip_free (serverip);
osip_free (netmask);
return 0;
}
return -1;
}
}
}
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_WARNING, NULL,
"Please define a default network interface.\r\n"));
#ifdef WANT_INTERFACE_ANYWAY
/* NT4 (sp4 support only?) */
if (!GetIpForwardTable (ipfwdt, &siz_ipfwd_table, FALSE))
{
for (ipf_cnt = 0; ipf_cnt < ipfwdt->dwNumEntries; ++ipf_cnt)
{
char *servername;
char *serverip;
char *netmask;
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO4, NULL,
"Default Interface found %i\r\n",
ipfwdt->table[ipf_cnt].dwForwardIfIndex));
if (0 == eXosip_dns_get_local_fqdn (&servername,
&serverip,
&netmask,
ipfwdt->table[ipf_cnt].
dwForwardIfIndex))
{
/* search for public */
if (eXosip_is_public_address (serverip) == 0)
{
osip_strncpy (address, serverip, size);
osip_free (servername);
osip_free (serverip);
osip_free (netmask);
return 0;
}
osip_free (servername);
osip_free (serverip);
osip_free (netmask);
}
}
}
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"No public interface found. searching private\r\n"));
/* NT4 (sp4 support only?) */
if (!GetIpForwardTable (ipfwdt, &siz_ipfwd_table, FALSE))
{
for (ipf_cnt = 0; ipf_cnt < ipfwdt->dwNumEntries; ++ipf_cnt)
{
char *servername;
char *serverip;
char *netmask;
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO4, NULL,
"Default Interface found %i\r\n",
ipfwdt->table[ipf_cnt].dwForwardIfIndex));
if (0 == eXosip_dns_get_local_fqdn (&servername,
&serverip,
&netmask,
ipfwdt->table[ipf_cnt].
dwForwardIfIndex))
{
osip_strncpy (address, serverip, size);
osip_free (servername);
osip_free (serverip);
osip_free (netmask);
return 0;
}
}
}
{
char *lo = osip_strdup ("127.0.0.1");
osip_strncpy (address, lo, size);
osip_free (lo);
return 0;
}
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"No interface found. returning 127.0.0.1\r\n"));
/* no default gateway interface found */
return 0;
#else
return -1;
#endif /* WANT_INTERFACE_ANYWAY */
}
#endif
#else /* sun, *BSD, linux, and other? */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <unistd.h>
#include <sys/param.h>
#include <stdio.h>
static int ppl_dns_default_gateway_ipv4 (char *address, int size);
static int ppl_dns_default_gateway_ipv6 (char *address, int size);
int
eXosip_guess_ip_for_via (int family, char *address, int size)
{
if (family == AF_INET6)
{
return ppl_dns_default_gateway_ipv6 (address, size);
} else
{
return ppl_dns_default_gateway_ipv4 (address, size);
}
}
/* This is a portable way to find the default gateway.
* The ip of the default interface is returned.
*/
static int
ppl_dns_default_gateway_ipv4 (char *address, int size)
{
#ifdef __APPLE_CC__
int len;
#else
unsigned int len;
#endif
int sock_rt, on = 1;
struct sockaddr_in iface_out;
struct sockaddr_in remote;
memset (&remote, 0, sizeof (struct sockaddr_in));
remote.sin_family = AF_INET;
remote.sin_addr.s_addr = inet_addr (eXosip.ipv4_for_gateway);
remote.sin_port = htons (11111);
memset (&iface_out, 0, sizeof (iface_out));
sock_rt = socket (AF_INET, SOCK_DGRAM, 0);
if (setsockopt (sock_rt, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) == -1)
{
perror ("DEBUG: [get_output_if] setsockopt(SOL_SOCKET, SO_BROADCAST");
close (sock_rt);
snprintf(address, size, "127.0.0.1");
return -1;
}
if (connect
(sock_rt, (struct sockaddr *) &remote, sizeof (struct sockaddr_in)) == -1)
{
perror ("DEBUG: [get_output_if] connect");
close (sock_rt);
snprintf(address, size, "127.0.0.1");
return -1;
}
len = sizeof (iface_out);
if (getsockname (sock_rt, (struct sockaddr *) &iface_out, &len) == -1)
{
perror ("DEBUG: [get_output_if] getsockname");
close (sock_rt);
snprintf(address, size, "127.0.0.1");
return -1;
}
close (sock_rt);
if (iface_out.sin_addr.s_addr == 0)
{ /* what is this case?? */
snprintf(address, size, "127.0.0.1");
return -1;
}
osip_strncpy (address, inet_ntoa (iface_out.sin_addr), size - 1);
return 0;
}
/* This is a portable way to find the default gateway.
* The ip of the default interface is returned.
*/
static int
ppl_dns_default_gateway_ipv6 (char *address, int size)
{
#ifdef __APPLE_CC__
int len;
#else
unsigned int len;
#endif
int sock_rt, on = 1;
struct sockaddr_in6 iface_out;
struct sockaddr_in6 remote;
memset (&remote, 0, sizeof (struct sockaddr_in6));
remote.sin6_family = AF_INET6;
inet_pton (AF_INET6, "2001:638:500:101:2e0:81ff:fe24:37c6", &remote.sin6_addr);
remote.sin6_port = htons (11111);
memset (&iface_out, 0, sizeof (iface_out));
sock_rt = socket (AF_INET6, SOCK_DGRAM, 0);
if (setsockopt (sock_rt, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) == -1)
{
perror ("DEBUG: [get_output_if] setsockopt(SOL_SOCKET, SO_BROADCAST");
close (sock_rt);
return -1;
}
if (connect
(sock_rt, (struct sockaddr *) &remote, sizeof (struct sockaddr_in6)) == -1)
{
perror ("DEBUG: [get_output_if] connect");
close (sock_rt);
return -1;
}
len = sizeof (iface_out);
if (getsockname (sock_rt, (struct sockaddr *) &iface_out, &len) == -1)
{
perror ("DEBUG: [get_output_if] getsockname");
close (sock_rt);
return -1;
}
close (sock_rt);
if (iface_out.sin6_addr.s6_addr == 0)
{ /* what is this case?? */
return -1;
}
inet_ntop (AF_INET6, (const void *) &iface_out.sin6_addr, address, size - 1);
return 0;
}
#endif
#ifdef SM
int
eXosip_get_localip_for (const char *address_to_reach, char *loc, int size)
{
int err, tmp;
struct addrinfo hints;
struct addrinfo *res = NULL;
struct sockaddr_storage addr;
int sock;
#ifdef __APPLE_CC__
int s;
#else
socklen_t s;
#endif
#ifdef MAXHOSTNAMELEN
if (size > MAXHOSTNAMELEN)
size = MAXHOSTNAMELEN;
#else
if (size > 256)
size = 256;
#endif
if (eXosip.forced_localip)
{
if (size > sizeof (eXosip.net_interfaces[0].net_firewall_ip))
size = sizeof (eXosip.net_interfaces[0].net_firewall_ip);
strncpy (loc, eXosip.net_interfaces[0].net_firewall_ip, size);
return 0;
}
strcpy (loc, "127.0.0.1"); /* always fallback to local loopback */
memset (&hints, 0, sizeof (hints));
if(ipv6_enable)
hints.ai_family = PF_INET6;
else
hints.ai_family = PF_INET; /* ipv4 only support */
hints.ai_socktype = SOCK_DGRAM;
/*hints.ai_flags=AI_NUMERICHOST|AI_CANONNAME; */
err = getaddrinfo (address_to_reach, "5060", &hints, &res);
if (err != 0)
{
eXosip_trace (OSIP_ERROR,
("Error in getaddrinfo for %s: %s\n", address_to_reach,
gai_strerror (err)));
return -1;
}
if (res == NULL)
{
eXosip_trace (OSIP_ERROR, ("getaddrinfo reported nothing !"));
return -1;
}
sock = socket (res->ai_family, SOCK_DGRAM, 0);
tmp = 1;
err =
setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char *) &tmp, sizeof (int));
if (err < 0)
{
eXosip_trace (OSIP_ERROR, ("Error in setsockopt: %s\n", strerror (errno)));
freeaddrinfo (res);
close (sock);
return -1;
}
err = connect (sock, res->ai_addr, res->ai_addrlen);
if (err < 0)
{
eXosip_trace (OSIP_ERROR, ("Error in connect: %s\n", strerror (errno)));
freeaddrinfo (res);
close (sock);
return -1;
}
freeaddrinfo (res);
res = NULL;
s = sizeof (addr);
err = getsockname (sock, (struct sockaddr *) &addr, &s);
if (err != 0)
{
eXosip_trace (OSIP_ERROR, ("Error in getsockname: %s\n", strerror (errno)));
close (sock);
return -1;
}
err =
getnameinfo ((struct sockaddr *) &addr, s, loc, size, NULL, 0, NI_NUMERICHOST);
if (err != 0)
{
eXosip_trace (OSIP_ERROR, ("getnameinfo error:%s", strerror (errno)));
close (sock);
return -1;
}
close (sock);
eXosip_trace (OSIP_INFO1,
("Outgoing interface to reach %s is %s.\n", address_to_reach,
loc));
return 0;
}
void
eXosip_get_localip_from_via (osip_message_t * mesg, char *locip, int size)
{
osip_via_t *via = NULL;
char *host;
via = (osip_via_t *) osip_list_get (mesg->vias, 0);
if (via == NULL)
{
host = "15.128.128.93";
eXosip_trace (OSIP_ERROR, ("Could not get via:%s"));
} else
host = via->host;
eXosip_get_localip_for (host, locip, size);
}
#endif
char *
strdup_printf (const char *fmt, ...)
{
/* Guess we need no more than 100 bytes. */
int n, size = 100;
char *p;
va_list ap;
if ((p = osip_malloc (size)) == NULL)
return NULL;
while (1)
{
/* Try to print in the allocated space. */
va_start (ap, fmt);
#ifdef WIN32
n = _vsnprintf (p, size, fmt, ap);
#else
n = vsnprintf (p, size, fmt, ap);
#endif
va_end (ap);
/* If that worked, return the string. */
if (n > -1 && n < size)
return p;
/* Else try again with more space. */
if (n > -1) /* glibc 2.1 */
size = n + 1; /* precisely what is needed */
else /* glibc 2.0 */
size *= 2; /* twice the old size */
if ((p = realloc (p, size)) == NULL)
return NULL;
}
}
int
eXosip_get_addrinfo (struct addrinfo **addrinfo, const char *hostname,
int service, int protocol)
{
struct addrinfo hints;
int error;
char portbuf[10];
if (hostname == NULL)
return -1;
if (service != -1) /* -1 for SRV record */
snprintf (portbuf, sizeof (portbuf), "%i", service);
memset (&hints, 0, sizeof (hints));
hints.ai_flags = 0;
if(ipv6_enable)
hints.ai_family = PF_INET6;
else
hints.ai_family = PF_INET; /* ipv4 only support */
if (protocol == IPPROTO_UDP)
hints.ai_socktype = SOCK_DGRAM;
else
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = protocol; /* IPPROTO_UDP or IPPROTO_TCP */
if (service == -1) /* -1 for SRV record */
{
error = getaddrinfo (hostname, "sip", &hints, addrinfo);
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO2, NULL,
"SRV resolution with udp-sip-%s\n", hostname));
} else
{
error = getaddrinfo (hostname, portbuf, &hints, addrinfo);
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO2, NULL,
"DNS resolution with %s:%i\n", hostname, service));
}
if (error || *addrinfo == NULL)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO2, NULL,
"getaddrinfo failure. %s:%s (%s)\n", hostname, portbuf,
gai_strerror (error)));
return -1;
}
return 0;
}
int
_eXosip_srv_lookup(osip_transaction_t * tr, osip_message_t * sip, struct osip_srv_record *record)
{
int use_srv=1;
int port;
char *host;
osip_via_t *via;
via = (osip_via_t *) osip_list_get (&sip->vias, 0);
if (via == NULL || via->protocol == NULL)
return -1;
if (MSG_IS_REQUEST(sip))
{
osip_route_t *route;
osip_message_get_route (sip, 0, &route);
if (route != NULL)
{
osip_uri_param_t *lr_param = NULL;
osip_uri_uparam_get_byname (route->url, "lr", &lr_param);
if (lr_param == NULL)
route = NULL;
}
if (route != NULL)
{
port = 5060;
if (route->url->port != NULL)
{
port = osip_atoi (route->url->port);
use_srv=0;
}
host = route->url->host;
}
else
{
port = 5060;
if (sip->req_uri->port != NULL)
{
port = osip_atoi (sip->req_uri->port);
use_srv=0;
}
host = sip->req_uri->host;
}
}
else
{
osip_generic_param_t *maddr;
osip_generic_param_t *received;
osip_generic_param_t *rport;
osip_via_param_get_byname (via, "maddr", &maddr);
osip_via_param_get_byname (via, "received", &received);
osip_via_param_get_byname (via, "rport", &rport);
if (maddr != NULL)
host = maddr->gvalue;
else if (received != NULL)
host = received->gvalue;
else
host = via->host;
if (via->port == NULL)
use_srv=0;
if (rport == NULL || rport->gvalue == NULL)
{
if (via->port != NULL)
port = osip_atoi (via->port);
else
port = 5060;
} else
port = osip_atoi (rport->gvalue);
}
if (use_srv==1)
{
int i;
i = _eXosip_get_srv_record(record, host, via->protocol);
}
return 0;
}
#if defined(WIN32) && !defined(_WIN32_WCE)
int
_eXosip_get_srv_record (struct osip_srv_record *record, char *domain, char *protocol)
{
char zone[1024];
PDNS_RECORD answer, tmp; /* answer buffer from nameserver */
int n;
char tr[100];
memset(record, 0, sizeof(struct osip_srv_record));
if (strlen(domain)+strlen(protocol)>1000)
return -1;
if (strlen(protocol)>=100)
return -1;
snprintf(tr, 100, protocol);
osip_tolower(tr);
snprintf(zone, 1024, "_sip._%s.%s", tr, domain);
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO2, NULL,
"About to ask for '%s IN SRV'\n", zone));
if (DnsQuery (zone, DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &answer, NULL) != 0)
{
return -1;
}
n = 0;
for (tmp = answer; tmp != NULL; tmp = tmp->pNext)
{
struct osip_srv_entry *srventry;
DNS_SRV_DATA * data;
if (tmp->wType != DNS_TYPE_SRV)
continue;
srventry = &record->srventry[n];
data=&tmp->Data.SRV;
snprintf(srventry->srv, sizeof(srventry->srv), "%s", data->pNameTarget);
srventry->priority = data->wPriority;
srventry->weight = data->wWeight;
if (srventry->weight)
srventry->rweight = 1 + rand () % (10000 * srventry->weight);
else
srventry->rweight = 0;
srventry->port = data->wPort;
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO2, NULL,
"SRV record %s IN SRV -> %s:%i/%i/%i/%i\n",
zone, srventry->srv, srventry->port, srventry->priority,
srventry->weight, srventry->rweight));
n++;
}
if (n==0)
return -1;
snprintf(record->name, sizeof(record->name), "%s", domain);
return 0;
}
#elif defined(__linux)
/* the biggest packet we'll send and receive */
#if PACKETSZ > 1024
#define MAXPACKET PACKETSZ
#else
#define MAXPACKET 1024
#endif
/* and what we send and receive */
typedef union
{
HEADER hdr;
u_char buf[MAXPACKET];
}
querybuf;
#ifndef T_SRV
#define T_SRV 33
#endif
int
_eXosip_get_srv_record (struct osip_srv_record *record, char *domain, char *protocol)
{
querybuf answer; /* answer buffer from nameserver */
int n;
char zone[1024];
int ancount, qdcount; /* answer count and query count */
HEADER *hp; /* answer buffer header */
char hostbuf[256];
unsigned char *msg, *eom, *cp; /* answer buffer positions */
int dlen, type, aclass, pref, weight, port;
long ttl;
int answerno;
char tr[100];
memset(record, 0, sizeof(struct osip_srv_record));
if (strlen(domain)+strlen(protocol)>1000)
return -1;
if (strlen(protocol)>=100)
return -1;
snprintf(tr, 100, protocol);
osip_tolower(tr);
snprintf(zone, 1024, "_sip._%s.%s", tr, domain);
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO2, NULL,
"About to ask for '%s IN SRV'\n", zone));
n = res_query (zone, C_IN, T_SRV, (unsigned char *) &answer, sizeof (answer));
if (n < (int) sizeof (HEADER))
{
return -1;
}
/* browse message and search for DNS answers part */
hp = (HEADER *) &answer;
qdcount = ntohs (hp->qdcount);
ancount = ntohs (hp->ancount);
msg = (unsigned char *) (&answer);
eom = (unsigned char *) (&answer) + n;
cp = (unsigned char *) (&answer) + sizeof (HEADER);
while (qdcount-- > 0 && cp < eom)
{
n = dn_expand (msg, eom, cp, (char *) hostbuf, 256);
if (n < 0)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_ERROR, NULL,
"Invalid SRV record answer for '%s': bad format\n", zone));
return -1;
}
cp += n + QFIXEDSZ;
}
/* browse DNS answers */
answerno = 0;
/* loop through the answer buffer and extract SRV records */
while (ancount-- > 0 && cp < eom)
{
struct osip_srv_entry *srventry;
n = dn_expand (msg, eom, cp, (char *) hostbuf, 256);
if (n < 0)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_ERROR, NULL,
"Invalid SRV record answer for '%s': bad format\n", zone));
return -1;
}
cp += n;
#if defined(__NetBSD__) || defined(__OpenBSD__) ||\
defined(OLD_NAMESER) || defined(__FreeBSD__)
type = _get_short (cp);
cp += sizeof (u_short);
#elif defined(__APPLE_CC__)
GETSHORT (type, cp);
#else
NS_GET16 (type, cp);
#endif
#if defined(__NetBSD__) || defined(__OpenBSD__) ||\
defined(OLD_NAMESER) || defined(__FreeBSD__)
aclass = _get_short (cp);
cp += sizeof (u_short);
#elif defined(__APPLE_CC__)
GETSHORT (aclass, cp);
#else
NS_GET16 (aclass, cp);
#endif
#if defined(__NetBSD__) || defined(__OpenBSD__) ||\
defined(OLD_NAMESER) || defined(__FreeBSD__)
ttl = _get_long (cp);
cp += sizeof (u_long);
#elif defined(__APPLE_CC__)
GETLONG (ttl, cp);
#else
NS_GET32 (ttl, cp);
#endif
#if defined(__NetBSD__) || defined(__OpenBSD__) ||\
defined(OLD_NAMESER) || defined(__FreeBSD__)
dlen = _get_short (cp);
cp += sizeof (u_short);
#elif defined(__APPLE_CC__)
GETSHORT (dlen, cp);
#else
NS_GET16 (dlen, cp);
#endif
if (type != T_SRV)
{
cp += dlen;
continue;
}
#if defined(__NetBSD__) || defined(__OpenBSD__) ||\
defined(OLD_NAMESER) || defined(__FreeBSD__)
pref = _get_short (cp);
cp += sizeof (u_short);
#elif defined(__APPLE_CC__)
GETSHORT (pref, cp);
#else
NS_GET16 (pref, cp);
#endif
#if defined(__NetBSD__) || defined(__OpenBSD__) ||\
defined(OLD_NAMESER) || defined(__FreeBSD__)
weight = _get_short (cp);
cp += sizeof (u_short);
#elif defined(__APPLE_CC__)
GETSHORT (weight, cp);
#else
NS_GET16 (weight, cp);
#endif
#if defined(__NetBSD__) || defined(__OpenBSD__) ||\
defined(OLD_NAMESER) || defined(__FreeBSD__)
port = _get_short (cp);
cp += sizeof (u_short);
#elif defined(__APPLE_CC__)
GETSHORT (port, cp);
#else
NS_GET16 (port, cp);
#endif
n = dn_expand (msg, eom, cp, (char *) hostbuf, 256);
if (n < 0)
break;
cp += n;
srventry = &record->srventry[answerno];
snprintf(srventry->srv, sizeof(srventry->srv), "%s", hostbuf);
srventry->priority = pref;
srventry->weight = weight;
if (weight)
srventry->rweight = 1 + random () % (10000 * srventry->weight);
else
srventry->rweight = 0;
srventry->port = port;
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO2, NULL,
"SRV record %s IN SRV -> %s:%i/%i/%i/%i\n",
zone, srventry->srv, srventry->port, srventry->priority,
srventry->weight, srventry->rweight));
answerno++;
}
if (answerno == 0)
return -1;
snprintf(record->name, sizeof(record->name), "%s", domain);
return 0;
}
#else
int
_eXosip_get_srv_record (struct osip_srv_record *record, char *domain, char *protocol)
{
return -1;
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1