2002-05-30 01:40:29 +00:00
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Various sorts of access control
*
* Copyright (C) 1999, Mark Spencer
*
* Mark Spencer <markster@linux-support.net>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <asterisk/acl.h>
#include <asterisk/logger.h>
2003-10-22 03:10:34 +00:00
#include <asterisk/channel.h>
2005-01-11 17:08:52 +00:00
#include <asterisk/options.h>
2004-05-09 08:22:15 +00:00
#include <asterisk/utils.h>
2004-06-22 18:49:00 +00:00
#include <asterisk/lock.h>
2004-12-03 23:34:45 +00:00
#include <asterisk/srv.h>
2002-05-30 01:40:29 +00:00
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
2003-05-04 05:52:52 +00:00
#include <net/if.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <sys/ioctl.h>
2004-02-02 06:39:23 +00:00
#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__)
2003-10-22 04:09:28 +00:00
#include <fcntl.h>
2003-10-22 03:10:34 +00:00
#include <net/route.h>
2004-06-09 01:45:08 +00:00
AST_MUTEX_DEFINE_STATIC ( routeseq_lock );
2003-10-22 03:10:34 +00:00
#endif
2002-05-30 01:40:29 +00:00
2004-12-14 23:36:30 +00:00
#if defined (SOLARIS)
#include <sys/sockio.h>
#endif
2005-01-11 17:08:52 +00:00
struct ast_netsock {
ASTOBJ_COMPONENTS ( struct ast_netsock );
struct sockaddr_in bindaddr ;
int sockfd ;
int * ioref ;
struct io_context * ioc ;
2005-03-09 05:48:11 +00:00
void * data ;
2005-01-11 17:08:52 +00:00
};
2004-12-14 23:36:30 +00:00
2002-05-30 01:40:29 +00:00
struct ast_ha {
/* Host access rule */
struct in_addr netaddr ;
struct in_addr netmask ;
int sense ;
struct ast_ha * next ;
};
2003-05-04 05:52:52 +00:00
/* Default IP - if not otherwise set, don't breathe garbage */
2003-05-08 14:53:15 +00:00
static struct in_addr __ourip = { 0x00000000 };
2003-05-04 05:52:52 +00:00
struct my_ifreq {
char ifrn_name [ IFNAMSIZ ]; /* Interface name, e.g. "eth0", "ppp0", etc. */
struct sockaddr_in ifru_addr ;
};
2004-05-27 20:53:02 +00:00
/* Free HA structure */
2002-05-30 01:40:29 +00:00
void ast_free_ha ( struct ast_ha * ha )
{
struct ast_ha * hal ;
while ( ha ) {
hal = ha ;
ha = ha -> next ;
free ( hal );
}
}
2004-05-27 20:53:02 +00:00
/* Copy HA structure */
static void ast_copy_ha ( struct ast_ha * from , struct ast_ha * to )
2004-06-22 20:11:15 +00:00
{
memcpy ( & to -> netaddr , & from -> netaddr , sizeof ( from -> netaddr ));
memcpy ( & to -> netmask , & from -> netmask , sizeof ( from -> netmask ));
to -> sense = from -> sense ;
}
2004-05-27 20:53:02 +00:00
/* Create duplicate of ha structure */
static struct ast_ha * ast_duplicate_ha ( struct ast_ha * original )
{
struct ast_ha * new_ha = malloc ( sizeof ( struct ast_ha ));
/* Copy from original to new object */
ast_copy_ha ( original , new_ha );
return ( new_ha );
}
/* Create duplicate HA link list */
/* Used in chan_sip2 templates */
struct ast_ha * ast_duplicate_ha_list ( struct ast_ha * original )
{
struct ast_ha * start = original ;
struct ast_ha * ret = NULL ;
2004-05-27 21:31:25 +00:00
struct ast_ha * link , * prev = NULL ;
2004-05-27 20:53:02 +00:00
while ( start ) {
link = ast_duplicate_ha ( start ); /* Create copy of this object */
2004-05-27 21:31:25 +00:00
if ( prev )
prev -> next = link ; /* Link previous to this object */
2004-05-27 20:53:02 +00:00
if ( ! ret )
ret = link ; /* Save starting point */
2004-06-22 20:11:15 +00:00
start = start -> next ; /* Go to next object */
prev = link ; /* Save pointer to this object */
}
return ( ret ); /* Return start of list */
2004-05-27 20:53:02 +00:00
}
2002-05-30 01:40:29 +00:00
struct ast_ha * ast_append_ha ( char * sense , char * stuff , struct ast_ha * path )
{
struct ast_ha * ha = malloc ( sizeof ( struct ast_ha ));
2004-05-27 21:31:25 +00:00
char * nm = "255.255.255.255" ;
2004-01-10 21:19:56 +00:00
char tmp [ 256 ] = "" ;
2002-05-30 01:40:29 +00:00
struct ast_ha * prev = NULL ;
struct ast_ha * ret ;
2004-01-10 21:43:17 +00:00
int x , z ;
unsigned int y ;
2002-05-30 01:40:29 +00:00
ret = path ;
while ( path ) {
prev = path ;
path = path -> next ;
}
if ( ha ) {
2004-01-10 21:19:56 +00:00
strncpy ( tmp , stuff , sizeof ( tmp ) - 1 );
nm = strchr ( tmp , '/' );
2002-05-30 01:40:29 +00:00
if ( ! nm )
nm = "255.255.255.255" ;
2004-01-10 21:19:56 +00:00
else {
* nm = '\0' ;
nm ++ ;
}
2004-01-10 21:43:17 +00:00
if ( ! strchr ( nm , '.' )) {
if (( sscanf ( nm , "%i" , & x ) == 1 ) && ( x >= 0 ) && ( x <= 32 )) {
y = 0 ;
for ( z = 0 ; z < x ; z ++ ) {
y >>= 1 ;
2004-01-10 22:19:31 +00:00
y |= 0x80000000 ;
2004-01-10 21:43:17 +00:00
}
ha -> netmask . s_addr = htonl ( y );
}
} else if ( ! inet_aton ( nm , & ha -> netmask )) {
2005-01-17 22:53:36 +00:00
ast_log ( LOG_WARNING , "%s is not a valid netmask \n " , nm );
2002-05-30 01:40:29 +00:00
free ( ha );
2004-01-10 21:19:56 +00:00
return path ;
2002-05-30 01:40:29 +00:00
}
2004-01-10 21:43:17 +00:00
if ( ! inet_aton ( tmp , & ha -> netaddr )) {
2005-01-17 22:53:36 +00:00
ast_log ( LOG_WARNING , "%s is not a valid IP \n " , tmp );
2002-05-30 01:40:29 +00:00
free ( ha );
2004-01-10 21:19:56 +00:00
return path ;
2002-05-30 01:40:29 +00:00
}
ha -> netaddr . s_addr &= ha -> netmask . s_addr ;
if ( ! strncasecmp ( sense , "p" , 1 )) {
ha -> sense = AST_SENSE_ALLOW ;
} else {
ha -> sense = AST_SENSE_DENY ;
}
ha -> next = NULL ;
if ( prev )
prev -> next = ha ;
else
ret = ha ;
}
2004-05-27 20:53:02 +00:00
ast_log ( LOG_DEBUG , "%s/%s appended to acl for peer \n " , stuff , nm );
2003-05-05 06:14:25 +00:00
return ret ;
2002-05-30 01:40:29 +00:00
}
int ast_apply_ha ( struct ast_ha * ha , struct sockaddr_in * sin )
{
/* Start optimistic */
int res = AST_SENSE_ALLOW ;
while ( ha ) {
2004-06-30 16:56:51 +00:00
char iabuf [ INET_ADDRSTRLEN ];
char iabuf2 [ INET_ADDRSTRLEN ];
2004-05-27 20:53:02 +00:00
/* DEBUG */
2004-06-30 16:56:51 +00:00
ast_log ( LOG_DEBUG ,
"##### Testing %s with %s \n " ,
ast_inet_ntoa ( iabuf , sizeof ( iabuf ), sin -> sin_addr ),
ast_inet_ntoa ( iabuf2 , sizeof ( iabuf2 ), ha -> netaddr ));
2002-05-30 01:40:29 +00:00
/* For each rule, if this address and the netmask = the net address
apply the current rule */
if (( sin -> sin_addr . s_addr & ha -> netmask . s_addr ) == ( ha -> netaddr . s_addr ))
res = ha -> sense ;
ha = ha -> next ;
}
return res ;
}
2004-12-03 23:34:45 +00:00
int ast_get_ip_or_srv ( struct sockaddr_in * sin , const char * value , const char * service )
2002-05-30 01:40:29 +00:00
{
struct hostent * hp ;
2004-04-22 00:20:34 +00:00
struct ast_hostent ahp ;
2004-12-03 23:34:45 +00:00
char srv [ 256 ];
char host [ 256 ];
int tportno = ntohs ( sin -> sin_port );
2005-02-13 19:55:28 +00:00
if ( inet_aton ( value , & sin -> sin_addr ))
return 0 ;
2004-12-03 23:34:45 +00:00
if ( service ) {
snprintf ( srv , sizeof ( srv ), "%s.%s" , service , value );
if ( ast_get_srv ( NULL , host , sizeof ( host ), & tportno , srv ) > 0 ) {
sin -> sin_port = htons ( tportno );
value = host ;
}
}
2004-04-22 00:20:34 +00:00
hp = ast_gethostbyname ( value , & ahp );
2002-05-30 01:40:29 +00:00
if ( hp ) {
memcpy ( & sin -> sin_addr , hp -> h_addr , sizeof ( sin -> sin_addr ));
} else {
ast_log ( LOG_WARNING , "Unable to lookup '%s' \n " , value );
return - 1 ;
}
return 0 ;
}
2004-12-03 23:34:45 +00:00
int ast_get_ip ( struct sockaddr_in * sin , const char * value )
{
return ast_get_ip_or_srv ( sin , value , NULL );
}
2003-05-04 05:52:52 +00:00
/* iface is the interface (e.g. eth0); address is the return value */
2005-01-11 17:08:52 +00:00
int ast_lookup_iface ( char * iface , struct in_addr * address )
{
2003-05-04 05:52:52 +00:00
int mysock , res = 0 ;
struct my_ifreq ifreq ;
memset ( & ifreq , 0 , sizeof ( ifreq ));
strncpy ( ifreq . ifrn_name , iface , sizeof ( ifreq . ifrn_name ) - 1 );
mysock = socket ( PF_INET , SOCK_DGRAM , IPPROTO_IP );
res = ioctl ( mysock , SIOCGIFADDR , & ifreq );
close ( mysock );
if ( res < 0 ) {
ast_log ( LOG_WARNING , "Unable to get IP of %s: %s \n " , iface , strerror ( errno ));
2003-05-04 06:05:45 +00:00
memcpy (( char * ) address ,( char * ) & __ourip , sizeof ( __ourip ));
2003-05-04 05:52:52 +00:00
return - 1 ;
} else {
2003-05-04 06:05:45 +00:00
memcpy (( char * ) address ,( char * ) & ifreq . ifru_addr . sin_addr , sizeof ( ifreq . ifru_addr . sin_addr ));
2003-05-04 05:52:52 +00:00
return 0 ;
}
}
int ast_ouraddrfor ( struct in_addr * them , struct in_addr * us )
{
2004-12-18 21:54:58 +00:00
int s ;
struct sockaddr_in sin ;
socklen_t slen ;
2003-10-22 03:10:34 +00:00
2004-12-18 21:54:58 +00:00
s = socket ( PF_INET , SOCK_DGRAM , 0 );
if ( s == - 1 ) {
ast_log ( LOG_WARNING , "Cannot create socket \n " );
2003-10-22 03:10:34 +00:00
return - 1 ;
}
2005-01-11 17:08:52 +00:00
sin . sin_family = AF_INET ;
sin . sin_port = 5060 ;
2004-12-18 21:54:58 +00:00
sin . sin_addr = * them ;
if ( connect ( s , ( struct sockaddr * ) & sin , sizeof ( sin ))) {
ast_log ( LOG_WARNING , "Cannot connect \n " );
2004-12-28 07:44:13 +00:00
close ( s );
2003-10-22 03:10:34 +00:00
return - 1 ;
}
2004-12-18 21:54:58 +00:00
slen = sizeof ( sin );
if ( getsockname ( s , ( struct sockaddr * ) & sin , & slen )) {
ast_log ( LOG_WARNING , "Cannot get socket name \n " );
2004-12-28 07:44:13 +00:00
close ( s );
2003-10-22 03:10:34 +00:00
return - 1 ;
}
2003-10-22 04:09:28 +00:00
close ( s );
2004-12-18 21:54:58 +00:00
* us = sin . sin_addr ;
2003-05-04 05:52:52 +00:00
return 0 ;
}
2005-01-11 17:08:52 +00:00
int ast_netsock_sockfd ( struct ast_netsock * ns )
{
if ( ns )
return ns -> sockfd ;
return - 1 ;
}
struct ast_netsock * ast_netsock_bindaddr ( struct ast_netsock_list * list , struct io_context * ioc , struct sockaddr_in * bindaddr , int tos , ast_io_cb callback , void * data )
{
int netsocket = - 1 ;
int * ioref ;
char iabuf [ INET_ADDRSTRLEN ];
struct ast_netsock * ns ;
/* Make a UDP socket */
netsocket = socket ( AF_INET , SOCK_DGRAM , IPPROTO_IP );
if ( netsocket < 0 ) {
ast_log ( LOG_ERROR , "Unable to create network socket: %s \n " , strerror ( errno ));
return NULL ;
}
if ( bind ( netsocket ,( struct sockaddr * ) bindaddr , sizeof ( struct sockaddr_in ))) {
ast_log ( LOG_ERROR , "Unable to bind to %s port %d: %s \n " , ast_inet_ntoa ( iabuf , sizeof ( iabuf ), bindaddr -> sin_addr ), ntohs ( bindaddr -> sin_port ), strerror ( errno ));
close ( netsocket );
return NULL ;
}
if ( option_verbose > 1 )
ast_verbose ( VERBOSE_PREFIX_2 "Using TOS bits %d \n " , tos );
if ( setsockopt ( netsocket , IPPROTO_IP , IP_TOS , & tos , sizeof ( tos )))
ast_log ( LOG_WARNING , "Unable to set TOS to %d \n " , tos );
ns = malloc ( sizeof ( struct ast_netsock ));
if ( ns ) {
2005-03-09 05:48:11 +00:00
/* Establish I/O callback for socket read */
ioref = ast_io_add ( ioc , netsocket , callback , AST_IO_IN , ns );
if ( ! ioref ) {
ast_log ( LOG_WARNING , "Out of memory! \n " );
close ( netsocket );
free ( ns );
return NULL ;
}
2005-01-11 17:08:52 +00:00
ASTOBJ_INIT ( ns );
ns -> ioref = ioref ;
ns -> ioc = ioc ;
ns -> sockfd = netsocket ;
2005-03-09 05:48:11 +00:00
ns -> data = data ;
2005-01-11 17:08:52 +00:00
memcpy ( & ns -> bindaddr , bindaddr , sizeof ( ns -> bindaddr ));
ASTOBJ_CONTAINER_LINK ( list , ns );
} else {
ast_log ( LOG_WARNING , "Out of memory! \n " );
close ( netsocket );
}
return ns ;
}
static void ast_netsock_destroy ( struct ast_netsock * netsock )
{
ast_io_remove ( netsock -> ioc , netsock -> ioref );
close ( netsock -> sockfd );
free ( netsock );
}
int ast_netsock_init ( struct ast_netsock_list * list )
{
memset ( list , 0 , sizeof ( struct ast_netsock_list ));
ASTOBJ_CONTAINER_INIT ( list );
return 0 ;
}
int ast_netsock_release ( struct ast_netsock_list * list )
{
ASTOBJ_CONTAINER_DESTROYALL ( list , ast_netsock_destroy );
ASTOBJ_CONTAINER_DESTROY ( list );
return 0 ;
}
2005-03-09 05:48:11 +00:00
const struct sockaddr_in * ast_netsock_boundaddr ( struct ast_netsock * ns )
{
return & ( ns -> bindaddr );
}
void * ast_netsock_data ( struct ast_netsock * ns )
{
return ns -> data ;
}
2005-01-11 17:08:52 +00:00
struct ast_netsock * ast_netsock_bind ( struct ast_netsock_list * list , struct io_context * ioc , const char * bindinfo , int defaultport , int tos , ast_io_cb callback , void * data )
{
struct sockaddr_in sin ;
char * tmp ;
char * port ;
int portno ;
memset ( & sin , 0 , sizeof ( sin ));
sin . sin_family = AF_INET ;
sin . sin_port = htons ( defaultport );
tmp = ast_strdupa ( bindinfo );
if ( tmp ) {
port = strchr ( tmp , ':' );
if ( port ) {
* port = '\0' ;
port ++ ;
if (( portno = atoi ( port )) > 0 )
sin . sin_port = htons ( portno );
}
2005-01-18 13:58:33 +00:00
inet_aton ( tmp , & sin . sin_addr );
2005-01-11 17:08:52 +00:00
return ast_netsock_bindaddr ( list , ioc , & sin , tos , callback , data );
} else
ast_log ( LOG_WARNING , "Out of memory! \n " );
return NULL ;
}
2005-02-09 03:22:42 +00:00
int ast_find_ourip ( struct in_addr * ourip , struct sockaddr_in bindaddr )
{
char ourhost [ 256 ];
struct ast_hostent ahp ;
struct hostent * hp ;
struct in_addr saddr ;
/* just use the bind address if it is nonzero */
if ( ntohl ( bindaddr . sin_addr . s_addr )) {
memcpy ( ourip , & bindaddr . sin_addr , sizeof ( * ourip ));
return 0 ;
}
/* try to use our hostname */
if ( gethostname ( ourhost , sizeof ( ourhost ))) {
ast_log ( LOG_WARNING , "Unable to get hostname \n " );
} else {
hp = ast_gethostbyname ( ourhost , & ahp );
if ( hp ) {
memcpy ( ourip , hp -> h_addr , sizeof ( * ourip ));
return 0 ;
}
}
/* A.ROOT-SERVERS.NET. */
if ( inet_aton ( "198.41.0.4" , & saddr ) && ! ast_ouraddrfor ( & saddr , ourip ))
return 0 ;
return - 1 ;
}