2020-04-12 17:44:27 +00:00
/* $Id: miniupnpd.c,v 1.243 2020/04/12 17:43:13 nanard Exp $ */
2017-05-27 08:25:53 +00:00
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* MiniUPnP project
2020-06-03 21:43:58 +00:00
* http : //miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
2020-04-09 18:41:14 +00:00
* ( c ) 2006 - 2020 Thomas Bernard
2011-09-28 19:13:20 +00:00
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
# include "config.h"
/* Experimental support for NFQUEUE interfaces */
# ifdef ENABLE_NFQUEUE
/* apt-get install libnetfilter-queue-dev */
# include <netinet/ip.h>
# include <netinet/udp.h>
2012-02-09 20:34:44 +00:00
#if 0
# include <linux/netfilter_ipv4.h> /* Defines verdicts (NF_ACCEPT, etc) */
# endif
2011-09-28 19:13:20 +00:00
# include <linux/netfilter.h>
# include <libnetfilter_queue/libnetfilter_queue.h>
# include <linux/netfilter/nfnetlink_queue.h>
# endif
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include <stdio.h>
# include <ctype.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <fcntl.h>
# include <sys/file.h>
# include <syslog.h>
# include <sys/time.h>
# include <time.h>
# include <signal.h>
# include <errno.h>
# include <sys/param.h>
# if defined(sun)
# include <kstat.h>
2014-10-21 08:56:34 +00:00
# elif !defined(__linux__)
2011-09-28 19:13:20 +00:00
/* for BSD's sysctl */
# include <sys/sysctl.h>
# endif
2020-04-13 01:39:01 +00:00
# ifdef HAS_LIBCAP
# include <sys/capability.h>
# endif
2020-05-06 22:10:49 +00:00
# ifdef HAS_LIBCAP_NG
# include <cap-ng.h>
# endif
2011-09-28 19:13:20 +00:00
/* unix sockets */
# ifdef USE_MINIUPNPDCTL
# include <sys/un.h>
# endif
2019-10-05 19:59:42 +00:00
# ifdef ENABLE_HTTPS
# include <openssl/crypto.h>
# endif
2011-09-28 19:13:20 +00:00
2015-06-09 13:13:25 +00:00
# ifdef TOMATO
2015-06-06 03:19:52 +00:00
# include <sys/stat.h>
2015-06-09 13:13:25 +00:00
# endif /* TOMATO */
2012-05-24 16:17:31 +00:00
# include "macros.h"
2011-09-28 19:13:20 +00:00
# include "upnpglobalvars.h"
# include "upnphttp.h"
# include "upnpdescgen.h"
# include "miniupnpdpath.h"
# include "getifaddr.h"
# include "upnpsoap.h"
# include "options.h"
# include "minissdp.h"
# include "upnpredirect.h"
2012-05-01 20:25:30 +00:00
# include "upnppinhole.h"
2018-05-19 11:32:42 +00:00
# include "upnpstun.h"
2011-09-28 19:13:20 +00:00
# include "miniupnpdtypes.h"
# include "daemonize.h"
# include "upnpevents.h"
2014-02-25 10:10:05 +00:00
# include "asyncsendto.h"
2011-09-28 19:13:20 +00:00
# ifdef ENABLE_NATPMP
# include "natpmp.h"
2013-07-09 13:36:53 +00:00
# ifdef ENABLE_PCP
# include "pcpserver.h"
# else
# define PCP_MAX_LEN 32
# endif
2011-09-28 19:13:20 +00:00
# endif
# include "commonrdr.h"
# include "upnputils.h"
# ifdef USE_IFACEWATCHER
# include "ifacewatcher.h"
# endif
2014-05-10 11:09:48 +00:00
# ifdef ENABLE_UPNPPINHOLE
2012-04-27 06:51:25 +00:00
# ifdef USE_NETFILTER
void init_iptpinhole ( void ) ;
# endif
# endif
2011-09-28 19:13:20 +00:00
# ifndef DEFAULT_CONFIG
# define DEFAULT_CONFIG " / etc / miniupnpd.conf"
# endif
# ifdef USE_MINIUPNPDCTL
struct ctlelem {
int socket ;
LIST_ENTRY ( ctlelem ) entries ;
} ;
2015-06-09 13:13:25 +00:00
# endif /* USE_MINIUPNPDCTL */
2011-09-28 19:13:20 +00:00
# ifdef ENABLE_NFQUEUE
/* globals */
static struct nfq_handle * nfqHandle ;
static struct sockaddr_in ssdp ;
/* prototypes */
static int nfqueue_cb ( struct nfq_q_handle * qh , struct nfgenmsg * nfmsg , struct nfq_data * nfa , void * data ) ;
int identify_ip_protocol ( char * payload ) ;
int get_udp_dst_port ( char * payload ) ;
2015-06-09 13:13:25 +00:00
# endif /* ENABLE_NFQUEUE */
2011-09-28 19:13:20 +00:00
/* variables used by signals */
2012-05-24 16:17:31 +00:00
static volatile sig_atomic_t quitting = 0 ;
volatile sig_atomic_t should_send_public_address_change_notif = 0 ;
2018-05-02 07:40:12 +00:00
# if !defined(TOMATO) && defined(ENABLE_LEASEFILE) && defined(LEASEFILE_USE_REMAINING_TIME)
volatile sig_atomic_t should_rewrite_leasefile = 0 ;
# endif /* !TOMATO && ENABLE_LEASEFILE && LEASEFILE_USE_REMAINING_TIME */
2011-09-28 19:13:20 +00:00
2015-06-06 03:19:52 +00:00
# ifdef TOMATO
# if 1
/* Tomato specific code */
2015-06-09 13:13:25 +00:00
static volatile sig_atomic_t gotusr2 = 0 ;
2015-06-06 03:19:52 +00:00
static void
sigusr2 ( int sig )
{
gotusr2 = 1 ;
}
static void
tomato_save ( const char * fname )
{
unsigned short eport ;
unsigned short iport ;
unsigned int leaseduration ;
unsigned int timestamp ;
char proto [ 4 ] ;
char iaddr [ 32 ] ;
char desc [ 64 ] ;
char rhost [ 32 ] ;
int n ;
FILE * f ;
int t ;
char tmp [ 128 ] ;
2015-06-09 13:13:25 +00:00
2015-06-06 03:19:52 +00:00
strcpy ( tmp , " /etc/upnp/saveXXXXXX " ) ;
2015-06-09 13:13:25 +00:00
if ( ( t = mkstemp ( tmp ) ) ! = - 1 )
2015-06-06 03:19:52 +00:00
{
2015-06-09 13:13:25 +00:00
if ( ( f = fdopen ( t , " w " ) ) ! = NULL )
2015-06-06 03:19:52 +00:00
{
n = 0 ;
2015-06-09 13:13:25 +00:00
while ( upnp_get_redirection_infos_by_index ( n , & eport , proto , & iport , iaddr , sizeof ( iaddr ) , desc , sizeof ( desc ) , rhost , sizeof ( rhost ) , & leaseduration ) = = 0 )
2015-06-06 03:19:52 +00:00
{
timestamp = ( leaseduration > 0 ) ? time ( NULL ) + leaseduration : 0 ;
fprintf ( f , " %s %u %s %u [%s] %u \n " , proto , eport , iaddr , iport , desc , timestamp ) ;
+ + n ;
}
fclose ( f ) ;
rename ( tmp , fname ) ;
}
2016-01-04 03:56:39 +00:00
else
2015-06-06 03:19:52 +00:00
{
close ( t ) ;
}
unlink ( tmp ) ;
}
}
static void
tomato_load ( void )
{
FILE * f ;
char s [ 256 ] ;
unsigned short eport ;
unsigned short iport ;
unsigned int leaseduration ;
unsigned int timestamp ;
time_t current_time ;
char proto [ 4 ] ;
char iaddr [ 32 ] ;
char * rhost ;
char * a , * b ;
if ( ( f = fopen ( " /etc/upnp/data " , " r " ) ) ! = NULL )
{
current_time = time ( NULL ) ;
s [ sizeof ( s ) - 1 ] = 0 ;
while ( fgets ( s , sizeof ( s ) - 1 , f ) ) {
2016-04-24 20:20:14 +00:00
if ( sscanf ( s , " %3s %hu %31s %hu [%*[^]]] %u " , proto , & eport , iaddr , & iport , & timestamp ) > = 4 )
2015-06-06 03:19:52 +00:00
{
if ( ( ( a = strchr ( s , ' [ ' ) ) ! = NULL ) & & ( ( b = strrchr ( a , ' ] ' ) ) ! = NULL ) )
{
if ( timestamp > 0 )
{
if ( timestamp > current_time )
2016-03-04 18:33:18 +00:00
leaseduration = timestamp - current_time ;
2015-06-06 03:19:52 +00:00
else
continue ;
}
else
{
leaseduration = 0 ; /* default value */
}
* b = 0 ;
rhost = NULL ;
2016-03-04 18:33:18 +00:00
syslog ( LOG_DEBUG , " Read redirection [%s] from file: %s port %hu to %s port %hu, timestamp: %u (%u) " ,
a + 1 , proto , eport , iaddr , iport , timestamp , leaseduration ) ;
2015-06-06 03:19:52 +00:00
upnp_redirect ( rhost , eport , iaddr , iport , proto , a + 1 , leaseduration ) ;
}
}
}
fclose ( f ) ;
}
# ifdef ENABLE_NATPMP
#if 0
ScanNATPMPforExpiration ( ) ;
# endif
2015-06-09 13:13:25 +00:00
# endif /* ENABLE_NATPMP */
2015-06-06 03:19:52 +00:00
unlink ( " /etc/upnp/load " ) ;
}
static void
tomato_delete ( void )
{
FILE * f ;
char s [ 128 ] ;
unsigned short eport ;
unsigned short iport ;
unsigned int leaseduration ;
char proto [ 4 ] ;
char iaddr [ 32 ] ;
char desc [ 64 ] ;
char rhost [ 32 ] ;
int n ;
if ( ( f = fopen ( " /etc/upnp/delete " , " r " ) ) ! = NULL )
{
s [ sizeof ( s ) - 1 ] = 0 ;
while ( fgets ( s , sizeof ( s ) - 1 , f ) )
{
if ( sscanf ( s , " %3s %hu " , proto , & eport ) = = 2 )
{
if ( proto [ 0 ] = = ' * ' )
{
n = upnp_get_portmapping_number_of_entries ( ) ;
while ( - - n > = 0 )
{
if ( upnp_get_redirection_infos_by_index ( n , & eport , proto , & iport , iaddr , sizeof ( iaddr ) , desc , sizeof ( desc ) , rhost , sizeof ( rhost ) , & leaseduration ) = = 0 )
{
upnp_delete_redirection ( eport , proto ) ;
}
}
break ;
}
else
{
upnp_delete_redirection ( eport , proto ) ;
}
}
}
fclose ( f ) ;
unlink ( " /etc/upnp/delete " ) ;
}
}
static void
tomato_helper ( void )
{
struct stat st ;
2016-01-04 03:56:39 +00:00
2015-06-06 03:19:52 +00:00
if ( stat ( " /etc/upnp/delete " , & st ) = = 0 )
{
tomato_delete ( ) ;
}
if ( stat ( " /etc/upnp/load " , & st ) = = 0 )
{
tomato_load ( ) ;
}
if ( stat ( " /etc/upnp/save " , & st ) = = 0 )
{
tomato_save ( " /etc/upnp/data " ) ;
unlink ( " /etc/upnp/save " ) ;
}
if ( stat ( " /etc/upnp/info " , & st ) = = 0 )
{
tomato_save ( " /etc/upnp/data.info " ) ;
unlink ( " /etc/upnp/info " ) ;
}
}
# endif /* 1 (tomato) */
# endif /* TOMATO */
2011-09-28 19:13:20 +00:00
/* OpenAndConfHTTPSocket() :
* setup the socket used to handle incoming HTTP connections . */
static int
2014-03-13 10:30:24 +00:00
# ifdef ENABLE_IPV6
2014-05-21 23:38:18 +00:00
OpenAndConfHTTPSocket ( unsigned short * port , int ipv6 )
2014-03-13 10:30:24 +00:00
# else
2014-05-21 23:38:18 +00:00
OpenAndConfHTTPSocket ( unsigned short * port )
2014-03-13 10:30:24 +00:00
# endif
2011-09-28 19:13:20 +00:00
{
int s ;
int i = 1 ;
# ifdef ENABLE_IPV6
2014-02-06 09:55:28 +00:00
struct sockaddr_in6 listenname6 ;
struct sockaddr_in listenname4 ;
2011-09-28 19:13:20 +00:00
# else
struct sockaddr_in listenname ;
# endif
socklen_t listenname_len ;
2014-05-21 23:38:18 +00:00
s = socket (
2011-09-28 19:13:20 +00:00
# ifdef ENABLE_IPV6
2014-05-21 23:38:18 +00:00
ipv6 ? PF_INET6 : PF_INET ,
2011-09-28 19:13:20 +00:00
# else
2014-05-21 23:38:18 +00:00
PF_INET ,
2011-09-28 19:13:20 +00:00
# endif
2014-05-21 23:38:18 +00:00
SOCK_STREAM , 0 ) ;
# ifdef ENABLE_IPV6
if ( s < 0 & & ipv6 & & errno = = EAFNOSUPPORT )
{
/* the system doesn't support IPV6 */
syslog ( LOG_WARNING , " socket(PF_INET6, ...) failed with EAFNOSUPPORT, disabling IPv6 " ) ;
SETFLAG ( IPV6DISABLEDMASK ) ;
ipv6 = 0 ;
2016-12-16 08:39:19 +00:00
/* Try again with IPv4 */
2014-05-21 23:38:18 +00:00
s = socket ( PF_INET , SOCK_STREAM , 0 ) ;
}
# endif
if ( s < 0 )
2011-09-28 19:13:20 +00:00
{
syslog ( LOG_ERR , " socket(http): %m " ) ;
return - 1 ;
}
if ( setsockopt ( s , SOL_SOCKET , SO_REUSEADDR , & i , sizeof ( i ) ) < 0 )
{
syslog ( LOG_WARNING , " setsockopt(http, SO_REUSEADDR): %m " ) ;
}
#if 0
/* enable this to force IPV6 only for IPV6 socket.
* see http : //www.ietf.org/rfc/rfc3493.txt section 5.3 */
if ( setsockopt ( s , IPPROTO_IPV6 , IPV6_V6ONLY , & i , sizeof ( i ) ) < 0 )
{
syslog ( LOG_WARNING , " setsockopt(http, IPV6_V6ONLY): %m " ) ;
}
# endif
2012-02-09 20:29:20 +00:00
if ( ! set_non_blocking ( s ) )
{
syslog ( LOG_WARNING , " set_non_blocking(http): %m " ) ;
}
2011-09-28 19:13:20 +00:00
# ifdef ENABLE_IPV6
2014-03-13 10:30:24 +00:00
if ( ipv6 )
2014-02-06 09:55:28 +00:00
{
memset ( & listenname6 , 0 , sizeof ( struct sockaddr_in6 ) ) ;
listenname6 . sin6_family = AF_INET6 ;
2014-05-21 23:38:18 +00:00
listenname6 . sin6_port = htons ( * port ) ;
2014-05-20 12:43:47 +00:00
listenname6 . sin6_addr = ipv6_bind_addr ;
2014-02-06 09:55:28 +00:00
listenname_len = sizeof ( struct sockaddr_in6 ) ;
} else {
memset ( & listenname4 , 0 , sizeof ( struct sockaddr_in ) ) ;
listenname4 . sin_family = AF_INET ;
2014-05-21 23:38:18 +00:00
listenname4 . sin_port = htons ( * port ) ;
2014-02-06 09:55:28 +00:00
listenname4 . sin_addr . s_addr = htonl ( INADDR_ANY ) ;
listenname_len = sizeof ( struct sockaddr_in ) ;
}
2011-09-28 19:13:20 +00:00
# else
2014-02-06 09:55:28 +00:00
memset ( & listenname , 0 , sizeof ( struct sockaddr_in ) ) ;
2011-09-28 19:13:20 +00:00
listenname . sin_family = AF_INET ;
2014-05-21 23:38:18 +00:00
listenname . sin_port = htons ( * port ) ;
2011-09-28 19:13:20 +00:00
listenname . sin_addr . s_addr = htonl ( INADDR_ANY ) ;
listenname_len = sizeof ( struct sockaddr_in ) ;
# endif
2015-08-26 07:46:05 +00:00
# if defined(SO_BINDTODEVICE) && !defined(MULTIPLE_EXTERNAL_IP)
/* One and only one LAN interface */
if ( lan_addrs . lh_first ! = NULL & & lan_addrs . lh_first - > list . le_next = = NULL
2018-06-01 08:55:45 +00:00
& & lan_addrs . lh_first - > ifname [ 0 ] ! = ' \0 ' )
2015-08-21 14:05:26 +00:00
{
2015-08-26 07:46:05 +00:00
if ( setsockopt ( s , SOL_SOCKET , SO_BINDTODEVICE ,
lan_addrs . lh_first - > ifname ,
strlen ( lan_addrs . lh_first - > ifname ) ) < 0 )
2015-08-26 08:04:23 +00:00
syslog ( LOG_WARNING , " setsockopt(http, SO_BINDTODEVICE, %s): %m " ,
lan_addrs . lh_first - > ifname ) ;
2015-08-21 14:05:26 +00:00
}
2015-08-26 07:46:05 +00:00
# endif /* defined(SO_BINDTODEVICE) && !defined(MULTIPLE_EXTERNAL_IP) */
2015-08-21 14:05:26 +00:00
2014-02-06 09:55:28 +00:00
# ifdef ENABLE_IPV6
if ( bind ( s ,
2014-03-13 10:30:24 +00:00
ipv6 ? ( struct sockaddr * ) & listenname6 : ( struct sockaddr * ) & listenname4 ,
2014-02-06 09:55:28 +00:00
listenname_len ) < 0 )
# else
2011-09-28 19:13:20 +00:00
if ( bind ( s , ( struct sockaddr * ) & listenname , listenname_len ) < 0 )
2014-02-06 09:55:28 +00:00
# endif
2011-09-28 19:13:20 +00:00
{
syslog ( LOG_ERR , " bind(http): %m " ) ;
close ( s ) ;
return - 1 ;
}
if ( listen ( s , 5 ) < 0 )
{
syslog ( LOG_ERR , " listen(http): %m " ) ;
close ( s ) ;
return - 1 ;
}
2014-05-21 23:38:18 +00:00
if ( * port = = 0 ) {
# ifdef ENABLE_IPV6
if ( ipv6 ) {
struct sockaddr_in6 sockinfo ;
socklen_t len = sizeof ( struct sockaddr_in6 ) ;
if ( getsockname ( s , ( struct sockaddr * ) & sockinfo , & len ) < 0 ) {
syslog ( LOG_ERR , " getsockname(): %m " ) ;
} else {
* port = ntohs ( sockinfo . sin6_port ) ;
}
} else {
# endif /* ENABLE_IPV6 */
struct sockaddr_in sockinfo ;
socklen_t len = sizeof ( struct sockaddr_in ) ;
if ( getsockname ( s , ( struct sockaddr * ) & sockinfo , & len ) < 0 ) {
syslog ( LOG_ERR , " getsockname(): %m " ) ;
} else {
* port = ntohs ( sockinfo . sin_port ) ;
}
# ifdef ENABLE_IPV6
}
# endif /* ENABLE_IPV6 */
}
2011-09-28 19:13:20 +00:00
return s ;
}
2014-02-06 09:55:28 +00:00
2014-03-13 10:30:24 +00:00
static struct upnphttp *
2014-04-20 16:40:26 +00:00
ProcessIncomingHTTP ( int shttpl , const char * protocol )
2014-03-13 10:30:24 +00:00
{
int shttp ;
socklen_t clientnamelen ;
# ifdef ENABLE_IPV6
struct sockaddr_storage clientname ;
clientnamelen = sizeof ( struct sockaddr_storage ) ;
# else
struct sockaddr_in clientname ;
clientnamelen = sizeof ( struct sockaddr_in ) ;
# endif
shttp = accept ( shttpl , ( struct sockaddr * ) & clientname , & clientnamelen ) ;
if ( shttp < 0 )
{
/* ignore EAGAIN, EWOULDBLOCK, EINTR, we just try again later */
if ( errno ! = EAGAIN & & errno ! = EWOULDBLOCK & & errno ! = EINTR )
syslog ( LOG_ERR , " accept(http): %m " ) ;
}
else
{
struct upnphttp * tmp = 0 ;
char addr_str [ 64 ] ;
sockaddr_to_string ( ( struct sockaddr * ) & clientname , addr_str , sizeof ( addr_str ) ) ;
2015-12-16 10:26:03 +00:00
# ifdef DEBUG
syslog ( LOG_DEBUG , " %s connection from %s " , protocol , addr_str ) ;
# endif /* DEBUG */
2014-03-13 10:30:24 +00:00
if ( get_lan_for_peer ( ( struct sockaddr * ) & clientname ) = = NULL )
{
/* The peer is not a LAN ! */
syslog ( LOG_WARNING ,
2014-04-20 16:40:26 +00:00
" %s peer %s is not from a LAN, closing the connection " ,
protocol , addr_str ) ;
2014-03-13 10:30:24 +00:00
close ( shttp ) ;
}
else
{
/* Create a new upnphttp object and add it to
* the active upnphttp object list */
tmp = New_upnphttp ( shttp ) ;
if ( tmp )
{
# ifdef ENABLE_IPV6
if ( clientname . ss_family = = AF_INET )
{
tmp - > clientaddr = ( ( struct sockaddr_in * ) & clientname ) - > sin_addr ;
}
else if ( clientname . ss_family = = AF_INET6 )
{
struct sockaddr_in6 * addr = ( struct sockaddr_in6 * ) & clientname ;
if ( IN6_IS_ADDR_V4MAPPED ( & addr - > sin6_addr ) )
{
memcpy ( & tmp - > clientaddr ,
& addr - > sin6_addr . s6_addr [ 12 ] ,
4 ) ;
}
else
{
tmp - > ipv6 = 1 ;
memcpy ( & tmp - > clientaddr_v6 ,
& addr - > sin6_addr ,
sizeof ( struct in6_addr ) ) ;
}
}
# else
tmp - > clientaddr = clientname . sin_addr ;
# endif
2015-12-16 10:26:03 +00:00
memcpy ( tmp - > clientaddr_str , addr_str , sizeof ( tmp - > clientaddr_str ) ) ;
2014-03-13 10:30:24 +00:00
return tmp ;
}
else
{
syslog ( LOG_ERR , " New_upnphttp() failed " ) ;
close ( shttp ) ;
}
}
}
return NULL ;
}
2011-09-28 19:13:20 +00:00
# ifdef ENABLE_NFQUEUE
int identify_ip_protocol ( char * payload ) {
return payload [ 9 ] ;
}
/*
* This function returns the destination port of the captured packet UDP
*/
int get_udp_dst_port ( char * payload ) {
char * pkt_data_ptr = NULL ;
pkt_data_ptr = payload + sizeof ( struct ip ) ;
/* Cast the UDP Header from the raw packet */
struct udphdr * udp = ( struct udphdr * ) pkt_data_ptr ;
/* get the dst port of the packet */
return ( ntohs ( udp - > dest ) ) ;
}
static int
OpenAndConfNFqueue ( ) {
struct nfq_q_handle * myQueue ;
struct nfnl_handle * netlinkHandle ;
int fd = 0 , e = 0 ;
inet_pton ( AF_INET , " 239.255.255.250 " , & ( ssdp . sin_addr ) ) ;
2012-02-04 23:40:50 +00:00
/* Get a queue connection handle from the module */
2011-09-28 19:13:20 +00:00
if ( ! ( nfqHandle = nfq_open ( ) ) ) {
syslog ( LOG_ERR , " Error in nfq_open(): %m " ) ;
return - 1 ;
}
2012-02-04 23:40:50 +00:00
/* Unbind the handler from processing any IP packets
Not totally sure why this is done , or if it ' s necessary . . . */
2011-09-28 19:13:20 +00:00
if ( ( e = nfq_unbind_pf ( nfqHandle , AF_INET ) ) < 0 ) {
syslog ( LOG_ERR , " Error in nfq_unbind_pf(): %m " ) ;
return - 1 ;
}
2012-02-04 23:40:50 +00:00
/* Bind this handler to process IP packets... */
2011-09-28 19:13:20 +00:00
if ( nfq_bind_pf ( nfqHandle , AF_INET ) < 0 ) {
syslog ( LOG_ERR , " Error in nfq_bind_pf(): %m " ) ;
return - 1 ;
}
2012-02-04 23:40:50 +00:00
/* Install a callback on queue -Q */
2011-09-28 19:13:20 +00:00
if ( ! ( myQueue = nfq_create_queue ( nfqHandle , nfqueue , & nfqueue_cb , NULL ) ) ) {
syslog ( LOG_ERR , " Error in nfq_create_queue(): %m " ) ;
return - 1 ;
}
2012-02-04 23:40:50 +00:00
/* Turn on packet copy mode */
2011-09-28 19:13:20 +00:00
if ( nfq_set_mode ( myQueue , NFQNL_COPY_PACKET , 0xffff ) < 0 ) {
syslog ( LOG_ERR , " Error setting packet copy mode (): %m " ) ;
return - 1 ;
}
netlinkHandle = nfq_nfnlh ( nfqHandle ) ;
fd = nfnl_fd ( netlinkHandle ) ;
return fd ;
}
static int nfqueue_cb (
struct nfq_q_handle * qh ,
struct nfgenmsg * nfmsg ,
struct nfq_data * nfa ,
void * data ) {
char * pkt ;
struct nfqnl_msg_packet_hdr * ph ;
ph = nfq_get_msg_packet_hdr ( nfa ) ;
if ( ph ) {
int id = 0 , size = 0 ;
id = ntohl ( ph - > packet_id ) ;
size = nfq_get_payload ( nfa , & pkt ) ;
struct ip * iph = ( struct ip * ) pkt ;
int id_protocol = identify_ip_protocol ( pkt ) ;
int dport = get_udp_dst_port ( pkt ) ;
int x = sizeof ( struct ip ) + sizeof ( struct udphdr ) ;
2012-03-01 01:44:38 +00:00
/* packets we are interested in are UDP multicast to 239.255.255.250:1900
2011-09-28 19:13:20 +00:00
* and start with a data string M - SEARCH
*/
2012-03-01 01:44:38 +00:00
if ( ( dport = = 1900 ) & & ( id_protocol = = IPPROTO_UDP )
2011-09-28 19:13:20 +00:00
& & ( ssdp . sin_addr . s_addr = = iph - > ip_dst . s_addr ) ) {
2012-03-01 01:44:38 +00:00
2011-09-28 19:13:20 +00:00
/* get the index that the packet came in on */
u_int32_t idx = nfq_get_indev ( nfa ) ;
int i = 0 ;
for ( ; i < n_nfqix ; i + + ) {
if ( nfqix [ i ] = = idx ) {
struct udphdr * udp = ( struct udphdr * ) ( pkt + sizeof ( struct ip ) ) ;
char * dd = pkt + x ;
2012-03-01 01:44:38 +00:00
2011-09-28 19:13:20 +00:00
struct sockaddr_in sendername ;
sendername . sin_family = AF_INET ;
sendername . sin_port = udp - > source ;
sendername . sin_addr . s_addr = iph - > ip_src . s_addr ;
/* printf("pkt found %s\n",dd);*/
ProcessSSDPData ( sudp , dd , size - x ,
2017-05-24 22:20:03 +00:00
& sendername , - 1 , ( unsigned short ) 5555 ) ;
2011-09-28 19:13:20 +00:00
}
}
}
2012-03-01 01:44:38 +00:00
2011-09-28 19:13:20 +00:00
nfq_set_verdict ( qh , id , NF_ACCEPT , 0 , NULL ) ;
} else {
syslog ( LOG_ERR , " nfq_get_msg_packet_hdr failed " ) ;
2012-02-04 23:40:50 +00:00
return 1 ;
/* from nfqueue source: 0 = ok, >0 = soft error, <0 hard error */
2011-09-28 19:13:20 +00:00
}
return 0 ;
}
static void ProcessNFQUEUE ( int fd ) {
char buf [ 4096 ] ;
socklen_t len_r ;
struct sockaddr_in sendername ;
len_r = sizeof ( struct sockaddr_in ) ;
int res = recvfrom ( fd , buf , sizeof ( buf ) , 0 ,
( struct sockaddr * ) & sendername , & len_r ) ;
nfq_handle_packet ( nfqHandle , buf , res ) ;
}
# endif
/* Functions used to communicate with miniupnpdctl */
# ifdef USE_MINIUPNPDCTL
static int
OpenAndConfCtlUnixSocket ( const char * path )
{
struct sockaddr_un localun ;
int s ;
s = socket ( AF_UNIX , SOCK_STREAM , 0 ) ;
localun . sun_family = AF_UNIX ;
strncpy ( localun . sun_path , path ,
sizeof ( localun . sun_path ) ) ;
if ( bind ( s , ( struct sockaddr * ) & localun ,
sizeof ( struct sockaddr_un ) ) < 0 )
{
syslog ( LOG_ERR , " bind(sctl): %m " ) ;
close ( s ) ;
s = - 1 ;
}
else if ( listen ( s , 5 ) < 0 )
{
syslog ( LOG_ERR , " listen(sctl): %m " ) ;
close ( s ) ;
s = - 1 ;
}
return s ;
}
static void
write_upnphttp_details ( int fd , struct upnphttp * e )
{
char buffer [ 256 ] ;
int len ;
write ( fd , " HTTP : \n " , 7 ) ;
while ( e )
{
len = snprintf ( buffer , sizeof ( buffer ) ,
" %d %d %s req_buf=%p(%dbytes) res_buf=%p(%dbytes alloc) \n " ,
e - > socket , e - > state , e - > HttpVer ,
e - > req_buf , e - > req_buflen ,
e - > res_buf , e - > res_buf_alloclen ) ;
write ( fd , buffer , len ) ;
e = e - > entries . le_next ;
}
}
static void
write_ctlsockets_list ( int fd , struct ctlelem * e )
{
char buffer [ 256 ] ;
int len ;
write ( fd , " CTL : \n " , 6 ) ;
while ( e )
{
len = snprintf ( buffer , sizeof ( buffer ) ,
" struct ctlelem: socket=%d \n " , e - > socket ) ;
write ( fd , buffer , len ) ;
e = e - > entries . le_next ;
}
}
2012-09-28 09:04:04 +00:00
# ifndef DISABLE_CONFIG_FILE
2011-09-28 19:13:20 +00:00
static void
write_option_list ( int fd )
{
char buffer [ 256 ] ;
int len ;
2012-07-17 19:37:09 +00:00
unsigned int i ;
2011-09-28 19:13:20 +00:00
write ( fd , " Options : \n " , 10 ) ;
for ( i = 0 ; i < num_options ; i + + )
{
len = snprintf ( buffer , sizeof ( buffer ) ,
" opt=%02d %s \n " ,
ary_options [ i ] . id , ary_options [ i ] . value ) ;
write ( fd , buffer , len ) ;
}
}
2012-09-28 09:04:04 +00:00
# endif
2011-09-28 19:13:20 +00:00
static void
write_command_line ( int fd , int argc , char * * argv )
{
char buffer [ 256 ] ;
int len ;
int i ;
write ( fd , " Command Line : \n " , 15 ) ;
for ( i = 0 ; i < argc ; i + + )
{
len = snprintf ( buffer , sizeof ( buffer ) ,
" argv[%02d]='%s' \n " ,
i , argv [ i ] ) ;
write ( fd , buffer , len ) ;
}
}
# endif
2012-03-01 01:44:38 +00:00
/* Handler for the SIGTERM signal (kill)
2011-09-28 19:13:20 +00:00
* SIGINT is also handled */
static void
sigterm ( int sig )
{
2012-05-24 16:17:31 +00:00
UNUSED ( sig ) ;
/*int save_errno = errno; */
/*signal(sig, SIG_IGN);*/ /* Ignore this signal while we are quitting */
/* Note : isn't it useless ? */
2011-09-28 19:13:20 +00:00
2012-05-24 16:17:31 +00:00
#if 0
/* calling syslog() is forbidden in signal handler according to
* signal ( 3 ) */
2011-09-28 19:13:20 +00:00
syslog ( LOG_NOTICE , " received signal %d, good-bye " , sig ) ;
2012-05-24 16:17:31 +00:00
# endif
2011-09-28 19:13:20 +00:00
quitting = 1 ;
/*errno = save_errno;*/
}
/* Handler for the SIGUSR1 signal indicating public IP address change. */
static void
sigusr1 ( int sig )
{
2012-05-24 16:17:31 +00:00
UNUSED ( sig ) ;
#if 0
/* calling syslog() is forbidden in signal handler according to
* signal ( 3 ) */
2011-09-28 19:13:20 +00:00
syslog ( LOG_INFO , " received signal %d, public ip address change " , sig ) ;
2012-05-24 16:17:31 +00:00
# endif
2011-09-28 19:13:20 +00:00
should_send_public_address_change_notif = 1 ;
}
2018-05-02 07:40:12 +00:00
# if !defined(TOMATO) && defined(ENABLE_LEASEFILE) && defined(LEASEFILE_USE_REMAINING_TIME)
/* Handler for the SIGUSR2 signal to request rewrite of lease_file */
static void
sigusr2 ( int sig )
{
UNUSED ( sig ) ;
should_rewrite_leasefile = 1 ;
}
# endif /* !TOMATO && ENABLE_LEASEFILE && LEASEFILE_USE_REMAINING_TIME */
2011-09-28 19:13:20 +00:00
/* record the startup time, for returning uptime */
static void
2018-03-13 10:43:07 +00:00
set_startup_time ( void )
2011-09-28 19:13:20 +00:00
{
2018-03-13 10:43:07 +00:00
startup_time = upnp_time ( ) ;
2014-12-10 09:41:10 +00:00
# ifdef USE_TIME_AS_BOOTID
2018-03-13 10:43:07 +00:00
if ( upnp_bootid = = 1 ) {
upnp_bootid = ( unsigned int ) time ( NULL ) ;
2014-12-10 09:41:10 +00:00
/* from UDA v1.1 :
* A convenient mechanism is to set this field value to the time
* that the device sends its initial announcement , expressed as
* seconds elapsed since midnight January 1 , 1970 ; */
}
# endif /* USE_TIME_AS_BOOTID */
2018-03-13 10:43:07 +00:00
if ( GETFLAG ( SYSUPTIMEMASK ) )
2011-09-28 19:13:20 +00:00
{
/* use system uptime instead of daemon uptime */
# if defined(__linux__)
2018-03-13 10:43:07 +00:00
unsigned long uptime = 0 ;
FILE * f = fopen ( " /proc/uptime " , " r " ) ;
if ( f = = NULL )
2011-09-28 19:13:20 +00:00
{
2018-03-13 10:43:07 +00:00
syslog ( LOG_ERR , " fopen( \" /proc/uptime \" ) : %m " ) ;
2011-09-28 19:13:20 +00:00
}
else
{
2018-03-13 10:43:07 +00:00
if ( fscanf ( f , " %lu " , & uptime ) ! = 1 )
2011-09-28 19:13:20 +00:00
{
2018-03-13 10:43:07 +00:00
syslog ( LOG_ERR , " fscanf( \" /proc/uptime \" ) : %m " ) ;
2011-09-28 19:13:20 +00:00
}
else
{
2018-03-13 10:43:07 +00:00
syslog ( LOG_INFO , " system uptime is %lu seconds " , uptime ) ;
2011-09-28 19:13:20 +00:00
}
2018-03-13 10:43:07 +00:00
fclose ( f ) ;
2011-09-28 19:13:20 +00:00
startup_time - = uptime ;
}
# elif defined(SOLARIS_KSTATS)
kstat_ctl_t * kc ;
kc = kstat_open ( ) ;
if ( kc ! = NULL )
{
kstat_t * ksp ;
ksp = kstat_lookup ( kc , " unix " , 0 , " system_misc " ) ;
if ( ksp & & ( kstat_read ( kc , ksp , NULL ) ! = - 1 ) )
{
void * ptr = kstat_data_lookup ( ksp , " boot_time " ) ;
if ( ptr )
memcpy ( & startup_time , ptr , sizeof ( startup_time ) ) ;
else
syslog ( LOG_ERR , " cannot find boot_time kstat " ) ;
}
else
syslog ( LOG_ERR , " cannot open kstats for unix/0/system_misc: %m " ) ;
kstat_close ( kc ) ;
}
# else
struct timeval boottime ;
size_t size = sizeof ( boottime ) ;
int name [ 2 ] = { CTL_KERN , KERN_BOOTTIME } ;
if ( sysctl ( name , 2 , & boottime , & size , NULL , 0 ) < 0 )
{
syslog ( LOG_ERR , " sysctl( \" kern.boottime \" ) failed " ) ;
}
else
{
startup_time = boottime . tv_sec ;
}
# endif
}
}
/* structure containing variables used during "main loop"
* that are filled during the init */
struct runtime_vars {
/* LAN IP addresses for SSDP traffic and HTTP */
/* moved to global vars */
int port ; /* HTTP Port */
2014-04-09 13:35:06 +00:00
# ifdef ENABLE_HTTPS
int https_port ; /* HTTPS Port */
# endif
2011-09-28 19:13:20 +00:00
int notify_interval ; /* seconds between SSDP announces */
/* unused rules cleaning related variables : */
int clean_ruleset_threshold ; /* threshold for removing unused rules */
int clean_ruleset_interval ; /* (minimum) interval between checks */
} ;
/* parselanaddr()
* parse address with mask
2012-02-15 22:48:20 +00:00
* ex : 192.168 .1 .1 / 24 or 192.168 .1 .1 / 255.255 .255 .0
2011-09-28 19:13:20 +00:00
* When MULTIPLE_EXTERNAL_IP is enabled , the ip address of the
* external interface associated with the lan subnet follows .
* ex : 192.168 .1 .1 / 24 81.21 .41 .11
*
2012-04-06 15:31:24 +00:00
* Can also use the interface name ( ie eth0 )
*
2012-03-01 01:44:38 +00:00
* return value :
2011-09-28 19:13:20 +00:00
* 0 : ok
* - 1 : error */
static int
parselanaddr ( struct lan_addr_s * lan_addr , const char * str )
{
const char * p ;
2020-04-28 22:03:54 +00:00
unsigned int n ;
2012-02-15 22:48:20 +00:00
char tmp [ 16 ] ;
2012-04-06 15:31:24 +00:00
memset ( lan_addr , 0 , sizeof ( struct lan_addr_s ) ) ;
2011-09-28 19:13:20 +00:00
p = str ;
while ( * p & & * p ! = ' / ' & & ! isspace ( * p ) )
p + + ;
n = p - str ;
2012-05-01 09:52:21 +00:00
if ( ! isdigit ( str [ 0 ] ) & & n < ( int ) sizeof ( lan_addr - > ifname ) )
2012-04-06 15:31:24 +00:00
{
/* not starting with a digit : suppose it is an interface name */
memcpy ( lan_addr - > ifname , str , n ) ;
lan_addr - > ifname [ n ] = ' \0 ' ;
2013-03-23 10:50:57 +00:00
if ( getifaddr ( lan_addr - > ifname , lan_addr - > str , sizeof ( lan_addr - > str ) ,
2015-02-20 17:31:55 +00:00
& lan_addr - > addr , & lan_addr - > mask ) < 0 ) {
# ifdef ENABLE_IPV6
fprintf ( stderr , " interface \" %s \" has no IPv4 address \n " , str ) ;
lan_addr - > str [ 0 ] = ' \0 ' ;
lan_addr - > addr . s_addr = htonl ( 0x00000000u ) ;
lan_addr - > mask . s_addr = htonl ( 0xffffffffu ) ;
# else /* ENABLE_IPV6 */
2012-04-06 15:31:24 +00:00
goto parselan_error ;
2015-02-20 17:31:55 +00:00
# endif /* ENABLE_IPV6 */
}
2014-05-22 07:42:05 +00:00
/*printf("%s => %s\n", lan_addr->ifname, lan_addr->str);*/
2012-04-06 15:31:24 +00:00
}
else
{
if ( n > 15 )
goto parselan_error ;
memcpy ( lan_addr - > str , str , n ) ;
lan_addr - > str [ n ] = ' \0 ' ;
2013-03-23 10:50:57 +00:00
if ( ! inet_aton ( lan_addr - > str , & lan_addr - > addr ) )
goto parselan_error ;
2012-04-06 15:31:24 +00:00
}
2012-02-15 22:48:20 +00:00
if ( * p = = ' / ' )
2011-09-28 19:13:20 +00:00
{
2012-02-15 22:48:20 +00:00
const char * q = + + p ;
while ( * p & & isdigit ( * p ) )
p + + ;
if ( * p = = ' . ' )
{
2014-02-03 09:44:24 +00:00
/* parse mask in /255.255.255.0 format */
2012-02-15 22:48:20 +00:00
while ( * p & & ( * p = = ' . ' | | isdigit ( * p ) ) )
p + + ;
n = p - q ;
2020-04-28 22:03:54 +00:00
if ( n > = sizeof ( tmp ) )
2012-02-15 22:48:20 +00:00
goto parselan_error ;
memcpy ( tmp , q , n ) ;
tmp [ n ] = ' \0 ' ;
if ( ! inet_aton ( tmp , & lan_addr - > mask ) )
goto parselan_error ;
}
else
{
2014-02-03 09:44:24 +00:00
/* it is a /24 format */
2012-02-15 22:48:20 +00:00
int nbits = atoi ( q ) ;
if ( nbits > 32 | | nbits < 0 )
goto parselan_error ;
lan_addr - > mask . s_addr = htonl ( nbits ? ( 0xffffffffu < < ( 32 - nbits ) ) : 0 ) ;
}
}
2013-03-23 10:50:57 +00:00
else if ( lan_addr - > mask . s_addr = = 0 )
2012-02-15 22:48:20 +00:00
{
/* by default, networks are /24 */
lan_addr - > mask . s_addr = htonl ( 0xffffff00u ) ;
2011-09-28 19:13:20 +00:00
}
# ifdef MULTIPLE_EXTERNAL_IP
/* skip spaces */
while ( * p & & isspace ( * p ) )
p + + ;
if ( * p ) {
/* parse the exteral ip address to associate with this subnet */
n = 0 ;
while ( p [ n ] & & ! isspace ( * p ) )
n + + ;
if ( n < = 15 ) {
memcpy ( lan_addr - > ext_ip_str , p , n ) ;
lan_addr - > ext_ip_str [ n ] = ' \0 ' ;
if ( ! inet_aton ( lan_addr - > ext_ip_str , & lan_addr - > ext_ip_addr ) ) {
/* error */
fprintf ( stderr , " Error parsing address : %s \n " , lan_addr - > ext_ip_str ) ;
2018-05-19 11:31:26 +00:00
return - 1 ;
}
if ( addr_is_reserved ( & lan_addr - > ext_ip_addr ) ) {
/* error */
fprintf ( stderr , " Error: option ext_ip address contains reserved / private address : %s \n " , lan_addr - > ext_ip_str ) ;
return - 1 ;
2011-09-28 19:13:20 +00:00
}
}
}
2020-04-28 22:03:54 +00:00
# else
while ( * p ) {
/* skip spaces */
while ( * p & & isspace ( * p ) )
p + + ;
if ( * p ) {
unsigned int index ;
n = 0 ;
while ( p [ n ] & & ! isspace ( p [ n ] ) & & n < sizeof ( tmp ) ) {
tmp [ n ] = p [ n ] ;
n + + ;
}
if ( n > = sizeof ( tmp ) ) {
fprintf ( stderr , " Cannot parse '%s' \n " , p ) ;
break ;
}
tmp [ n ] = ' \0 ' ;
index = if_nametoindex ( tmp ) ;
if ( index = = 0 ) {
fprintf ( stderr , " Cannot get index for network interface %s \n " ,
tmp ) ;
} else {
lan_addr - > add_indexes | = ( 1UL < < ( index - 1 ) ) ;
}
p + = n ;
}
}
2012-04-06 15:31:24 +00:00
# endif
if ( lan_addr - > ifname [ 0 ] ! = ' \0 ' )
{
lan_addr - > index = if_nametoindex ( lan_addr - > ifname ) ;
if ( lan_addr - > index = = 0 )
2020-04-28 22:03:54 +00:00
fprintf ( stderr , " Cannot get index for network interface %s \n " ,
2012-04-06 15:31:24 +00:00
lan_addr - > ifname ) ;
}
2017-05-24 22:20:03 +00:00
# ifdef ENABLE_IPV6
2013-04-29 10:22:24 +00:00
else
{
2014-05-22 07:42:05 +00:00
fprintf ( stderr ,
" Error: please specify LAN network interface by name instead of IPv4 address : %s \n " ,
str ) ;
return - 1 ;
2013-04-29 10:22:24 +00:00
}
2018-04-12 08:49:53 +00:00
# else
else
{
syslog ( LOG_NOTICE , " it is advised to use network interface name instead of %s " , str ) ;
}
2011-09-28 19:13:20 +00:00
# endif
return 0 ;
2012-02-15 22:48:20 +00:00
parselan_error :
2012-04-06 15:31:24 +00:00
fprintf ( stderr , " Error parsing address/mask (or interface name) : %s \n " ,
str ) ;
2012-02-15 22:48:20 +00:00
return - 1 ;
2011-09-28 19:13:20 +00:00
}
2018-05-19 11:32:42 +00:00
static char ext_addr_str [ INET_ADDRSTRLEN ] ;
int update_ext_ip_addr_from_stun ( int init )
{
struct in_addr if_addr , ext_addr ;
int restrictive_nat ;
char if_addr_str [ INET_ADDRSTRLEN ] ;
syslog ( LOG_INFO , " STUN: Performing with host=%s and port=%u ... " , ext_stun_host , ( unsigned ) ext_stun_port ) ;
if ( getifaddr ( ext_if_name , if_addr_str , INET_ADDRSTRLEN , & if_addr , NULL ) < 0 ) {
syslog ( LOG_ERR , " STUN: Cannot get IP address for ext interface %s " , ext_if_name ) ;
return 1 ;
}
if ( perform_stun ( ext_if_name , if_addr_str , ext_stun_host , ext_stun_port , & ext_addr , & restrictive_nat ) ! = 0 ) {
syslog ( LOG_ERR , " STUN: Performing STUN failed: %s " , strerror ( errno ) ) ;
return 1 ;
}
if ( ! inet_ntop ( AF_INET , & ext_addr , ext_addr_str , sizeof ( ext_addr_str ) ) ) {
syslog ( LOG_ERR , " STUN: Function inet_ntop for IP address returned by STUN failed: %s " , strerror ( errno ) ) ;
return 1 ;
}
if ( ( init | | disable_port_forwarding ) & & ! restrictive_nat ) {
if ( addr_is_reserved ( & if_addr ) )
syslog ( LOG_INFO , " STUN: ext interface %s with IP address %s is now behind unrestricted NAT 1:1 with public IP address %s: Port forwarding is now enabled " , ext_if_name , if_addr_str , ext_addr_str ) ;
else
syslog ( LOG_INFO , " STUN: ext interface %s has now public IP address %s: Port forwarding is now enabled " , ext_if_name , if_addr_str ) ;
} else if ( ( init | | ! disable_port_forwarding ) & & restrictive_nat ) {
2018-07-06 12:40:53 +00:00
syslog ( LOG_WARNING , " STUN: ext interface %s with IP address %s is now behind restrictive NAT with public IP address %s: Port forwarding is now impossible " , ext_if_name , if_addr_str , ext_addr_str ) ;
2018-07-06 11:29:33 +00:00
} else {
syslog ( LOG_INFO , " STUN: ... done " ) ;
2018-05-19 11:32:42 +00:00
}
2018-07-06 11:29:33 +00:00
use_ext_ip_addr = ext_addr_str ;
2018-05-19 11:32:42 +00:00
disable_port_forwarding = restrictive_nat ;
return 0 ;
}
2013-06-13 14:03:36 +00:00
/* fill uuidvalue_wan and uuidvalue_wcd based on uuidvalue_igd */
void complete_uuidvalues ( void )
{
size_t len ;
len = strlen ( uuidvalue_igd ) ;
memcpy ( uuidvalue_wan , uuidvalue_igd , len + 1 ) ;
switch ( uuidvalue_wan [ len - 1 ] ) {
case ' 9 ' :
uuidvalue_wan [ len - 1 ] = ' a ' ;
break ;
case ' f ' :
uuidvalue_wan [ len - 1 ] = ' 0 ' ;
break ;
default :
uuidvalue_wan [ len - 1 ] + + ;
}
memcpy ( uuidvalue_wcd , uuidvalue_wan , len + 1 ) ;
switch ( uuidvalue_wcd [ len - 1 ] ) {
case ' 9 ' :
uuidvalue_wcd [ len - 1 ] = ' a ' ;
break ;
case ' f ' :
uuidvalue_wcd [ len - 1 ] = ' 0 ' ;
break ;
default :
uuidvalue_wcd [ len - 1 ] + + ;
}
}
2011-09-28 19:13:20 +00:00
/* init phase :
* 1 ) read configuration file
* 2 ) read command line arguments
* 3 ) daemonize
* 4 ) open syslog
* 5 ) check and write pid file
* 6 ) set startup time stamp
* 7 ) compute presentation URL
2014-02-25 09:23:59 +00:00
* 8 ) set signal handlers
2014-02-25 09:24:22 +00:00
* 9 ) init random generator ( srandom ( ) )
2014-02-25 09:23:59 +00:00
* 10 ) init redirection engine
* 11 ) reload mapping from leasefile */
2011-09-28 19:13:20 +00:00
static int
init ( int argc , char * * argv , struct runtime_vars * v )
{
int i ;
2020-06-03 21:43:58 +00:00
# ifndef NO_BACKGROUND_NO_PIDFILE
2011-09-28 19:13:20 +00:00
int pid ;
2020-06-03 21:43:58 +00:00
# endif
2011-09-28 19:13:20 +00:00
int debug_flag = 0 ;
int openlog_option ;
2018-05-19 11:31:26 +00:00
struct in_addr addr ;
2011-09-28 19:13:20 +00:00
struct sigaction sa ;
/*const char * logfilename = 0;*/
const char * presurl = 0 ;
2012-06-29 19:39:34 +00:00
# ifndef DISABLE_CONFIG_FILE
int options_flag = 0 ;
2011-09-28 19:13:20 +00:00
const char * optionsfile = DEFAULT_CONFIG ;
2012-06-29 19:39:34 +00:00
# endif /* DISABLE_CONFIG_FILE */
2011-09-28 19:13:20 +00:00
struct lan_addr_s * lan_addr ;
struct lan_addr_s * lan_addr2 ;
/* only print usage if -h is used */
for ( i = 1 ; i < argc ; i + + )
{
if ( 0 = = strcmp ( argv [ i ] , " -h " ) )
goto print_usage ;
}
2012-06-29 19:39:34 +00:00
# ifndef DISABLE_CONFIG_FILE
2011-09-28 19:13:20 +00:00
/* first check if "-f" option is used */
for ( i = 2 ; i < argc ; i + + )
{
if ( 0 = = strcmp ( argv [ i - 1 ] , " -f " ) )
{
optionsfile = argv [ i ] ;
options_flag = 1 ;
break ;
}
}
2012-06-29 19:39:34 +00:00
# endif /* DISABLE_CONFIG_FILE */
2011-09-28 19:13:20 +00:00
/* set initial values */
2012-06-29 19:39:34 +00:00
SETFLAG ( ENABLEUPNPMASK ) ; /* UPnP is enabled by default */
2014-05-20 12:43:47 +00:00
# ifdef ENABLE_IPV6
ipv6_bind_addr = in6addr_any ;
# endif /* ENABLE_IPV6 */
2011-09-28 19:13:20 +00:00
LIST_INIT ( & lan_addrs ) ;
v - > port = - 1 ;
2014-04-09 13:35:06 +00:00
# ifdef ENABLE_HTTPS
v - > https_port = - 1 ;
# endif
2011-09-28 19:13:20 +00:00
v - > notify_interval = 30 ; /* seconds between SSDP announces */
v - > clean_ruleset_threshold = 20 ;
v - > clean_ruleset_interval = 0 ; /* interval between ruleset check. 0=disabled */
2012-06-29 19:39:34 +00:00
# ifndef DISABLE_CONFIG_FILE
2011-09-28 19:13:20 +00:00
/* read options file first since
* command line arguments have final say */
if ( readoptionsfile ( optionsfile ) < 0 )
{
/* only error if file exists or using -f */
if ( access ( optionsfile , F_OK ) = = 0 | | options_flag )
fprintf ( stderr , " Error reading configuration file %s \n " , optionsfile ) ;
}
else
{
2012-05-01 09:52:21 +00:00
for ( i = 0 ; i < ( int ) num_options ; i + + )
2011-09-28 19:13:20 +00:00
{
switch ( ary_options [ i ] . id )
{
case UPNPEXT_IFNAME :
ext_if_name = ary_options [ i ] . value ;
break ;
2019-05-21 08:42:40 +00:00
# ifdef ENABLE_IPV6
case UPNPEXT_IFNAME6 :
ext_if_name6 = ary_options [ i ] . value ;
break ;
# endif
2011-09-28 19:13:20 +00:00
case UPNPEXT_IP :
use_ext_ip_addr = ary_options [ i ] . value ;
break ;
2018-05-19 11:32:42 +00:00
case UPNPEXT_PERFORM_STUN :
if ( strcmp ( ary_options [ i ] . value , " yes " ) = = 0 )
2018-07-06 11:23:22 +00:00
SETFLAG ( PERFORMSTUNMASK ) ;
2018-05-19 11:32:42 +00:00
break ;
case UPNPEXT_STUN_HOST :
ext_stun_host = ary_options [ i ] . value ;
break ;
case UPNPEXT_STUN_PORT :
ext_stun_port = atoi ( ary_options [ i ] . value ) ;
break ;
2011-09-28 19:13:20 +00:00
case UPNPLISTENING_IP :
lan_addr = ( struct lan_addr_s * ) malloc ( sizeof ( struct lan_addr_s ) ) ;
if ( lan_addr = = NULL )
{
fprintf ( stderr , " malloc(sizeof(struct lan_addr_s)): %m " ) ;
break ;
}
if ( parselanaddr ( lan_addr , ary_options [ i ] . value ) ! = 0 )
{
2014-05-22 07:42:05 +00:00
fprintf ( stderr , " can't parse \" %s \" as a valid "
# ifndef ENABLE_IPV6
" LAN address or "
# endif
" interface name \n " , ary_options [ i ] . value ) ;
2011-09-28 19:13:20 +00:00
free ( lan_addr ) ;
break ;
}
LIST_INSERT_HEAD ( & lan_addrs , lan_addr , list ) ;
break ;
2014-05-20 12:43:47 +00:00
# ifdef ENABLE_IPV6
case UPNPIPV6_LISTENING_IP :
if ( inet_pton ( AF_INET6 , ary_options [ i ] . value , & ipv6_bind_addr ) < 1 )
{
fprintf ( stderr , " can't parse \" %s \" as valid IPv6 listening address " , ary_options [ i ] . value ) ;
}
break ;
2020-04-09 18:41:14 +00:00
case UPNPIPV6_DISABLE :
if ( strcmp ( ary_options [ i ] . value , " yes " ) = = 0 )
SETFLAG ( IPV6DISABLEDMASK ) ;
break ;
2014-05-20 12:43:47 +00:00
# endif /* ENABLE_IPV6 */
2011-09-28 19:13:20 +00:00
case UPNPPORT :
v - > port = atoi ( ary_options [ i ] . value ) ;
break ;
2014-04-20 16:06:00 +00:00
# ifdef ENABLE_HTTPS
case UPNPHTTPSPORT :
v - > https_port = atoi ( ary_options [ i ] . value ) ;
break ;
# endif
2011-09-28 19:13:20 +00:00
case UPNPBITRATE_UP :
upstream_bitrate = strtoul ( ary_options [ i ] . value , 0 , 0 ) ;
break ;
case UPNPBITRATE_DOWN :
downstream_bitrate = strtoul ( ary_options [ i ] . value , 0 , 0 ) ;
break ;
case UPNPPRESENTATIONURL :
presurl = ary_options [ i ] . value ;
break ;
2013-12-13 11:03:28 +00:00
# ifdef ENABLE_MANUFACTURER_INFO_CONFIGURATION
2012-02-04 23:24:13 +00:00
case UPNPFRIENDLY_NAME :
strncpy ( friendly_name , ary_options [ i ] . value , FRIENDLY_NAME_MAX_LEN ) ;
friendly_name [ FRIENDLY_NAME_MAX_LEN - 1 ] = ' \0 ' ;
break ;
2013-10-20 21:02:19 +00:00
case UPNPMANUFACTURER_NAME :
strncpy ( manufacturer_name , ary_options [ i ] . value , MANUFACTURER_NAME_MAX_LEN ) ;
manufacturer_name [ MANUFACTURER_NAME_MAX_LEN - 1 ] = ' \0 ' ;
break ;
case UPNPMANUFACTURER_URL :
strncpy ( manufacturer_url , ary_options [ i ] . value , MANUFACTURER_URL_MAX_LEN ) ;
manufacturer_url [ MANUFACTURER_URL_MAX_LEN - 1 ] = ' \0 ' ;
break ;
case UPNPMODEL_NAME :
strncpy ( model_name , ary_options [ i ] . value , MODEL_NAME_MAX_LEN ) ;
model_name [ MODEL_NAME_MAX_LEN - 1 ] = ' \0 ' ;
break ;
case UPNPMODEL_DESCRIPTION :
strncpy ( model_description , ary_options [ i ] . value , MODEL_DESCRIPTION_MAX_LEN ) ;
model_description [ MODEL_DESCRIPTION_MAX_LEN - 1 ] = ' \0 ' ;
break ;
case UPNPMODEL_URL :
strncpy ( model_url , ary_options [ i ] . value , MODEL_URL_MAX_LEN ) ;
model_url [ MODEL_URL_MAX_LEN - 1 ] = ' \0 ' ;
break ;
2015-06-09 13:13:25 +00:00
# endif /* ENABLE_MANUFACTURER_INFO_CONFIGURATION */
2011-09-28 19:13:20 +00:00
# ifdef USE_NETFILTER
2019-09-30 07:12:08 +00:00
# ifdef USE_NFTABLES
case UPNPFORWARDCHAIN :
2019-10-02 20:29:29 +00:00
set_rdr_name ( RDR_FORWARD_CHAIN_NAME , ary_options [ i ] . value ) ;
2019-09-30 07:12:08 +00:00
break ;
case UPNPNATCHAIN :
2019-10-02 20:29:29 +00:00
set_rdr_name ( RDR_NAT_PREROUTING_CHAIN_NAME , ary_options [ i ] . value ) ;
2019-09-30 07:12:08 +00:00
break ;
case UPNPNATPOSTCHAIN :
2019-10-02 20:29:29 +00:00
set_rdr_name ( RDR_NAT_POSTROUTING_CHAIN_NAME , ary_options [ i ] . value ) ;
2019-09-30 07:12:08 +00:00
break ;
# else
2011-09-28 19:13:20 +00:00
case UPNPFORWARDCHAIN :
miniupnpd_forward_chain = ary_options [ i ] . value ;
break ;
case UPNPNATCHAIN :
miniupnpd_nat_chain = ary_options [ i ] . value ;
break ;
2016-01-26 15:59:04 +00:00
case UPNPNATPOSTCHAIN :
miniupnpd_nat_postrouting_chain = ary_options [ i ] . value ;
2011-09-28 19:13:20 +00:00
break ;
2019-09-30 07:12:08 +00:00
# endif /* else USE_NFTABLES */
# endif /* USE_NETFILTER */
2011-09-28 19:13:20 +00:00
case UPNPNOTIFY_INTERVAL :
v - > notify_interval = atoi ( ary_options [ i ] . value ) ;
break ;
case UPNPSYSTEM_UPTIME :
if ( strcmp ( ary_options [ i ] . value , " yes " ) = = 0 )
SETFLAG ( SYSUPTIMEMASK ) ; /*sysuptime = 1;*/
break ;
2012-06-29 19:47:25 +00:00
# if defined(USE_PF) || defined(USE_IPF)
2011-09-28 19:13:20 +00:00
case UPNPPACKET_LOG :
if ( strcmp ( ary_options [ i ] . value , " yes " ) = = 0 )
SETFLAG ( LOGPACKETSMASK ) ; /*logpackets = 1;*/
break ;
2015-06-09 13:13:25 +00:00
# endif /* defined(USE_PF) || defined(USE_IPF) */
2011-09-28 19:13:20 +00:00
case UPNPUUID :
2013-06-13 14:03:36 +00:00
strncpy ( uuidvalue_igd + 5 , ary_options [ i ] . value ,
strlen ( uuidvalue_igd + 5 ) + 1 ) ;
complete_uuidvalues ( ) ;
2011-09-28 19:13:20 +00:00
break ;
case UPNPSERIAL :
strncpy ( serialnumber , ary_options [ i ] . value , SERIALNUMBER_MAX_LEN ) ;
serialnumber [ SERIALNUMBER_MAX_LEN - 1 ] = ' \0 ' ;
2012-03-01 01:44:38 +00:00
break ;
2011-09-28 19:13:20 +00:00
case UPNPMODEL_NUMBER :
strncpy ( modelnumber , ary_options [ i ] . value , MODELNUMBER_MAX_LEN ) ;
modelnumber [ MODELNUMBER_MAX_LEN - 1 ] = ' \0 ' ;
break ;
case UPNPCLEANTHRESHOLD :
v - > clean_ruleset_threshold = atoi ( ary_options [ i ] . value ) ;
break ;
case UPNPCLEANINTERVAL :
v - > clean_ruleset_interval = atoi ( ary_options [ i ] . value ) ;
break ;
# ifdef USE_PF
2012-02-03 12:14:10 +00:00
case UPNPANCHOR :
anchor_name = ary_options [ i ] . value ;
break ;
2011-09-28 19:13:20 +00:00
case UPNPQUEUE :
queue = ary_options [ i ] . value ;
break ;
case UPNPTAG :
tag = ary_options [ i ] . value ;
break ;
2015-06-09 13:13:25 +00:00
# endif /* USE_PF */
2011-09-28 19:13:20 +00:00
# ifdef ENABLE_NATPMP
case UPNPENABLENATPMP :
if ( strcmp ( ary_options [ i ] . value , " yes " ) = = 0 )
SETFLAG ( ENABLENATPMPMASK ) ; /*enablenatpmp = 1;*/
else
if ( atoi ( ary_options [ i ] . value ) )
SETFLAG ( ENABLENATPMPMASK ) ;
/*enablenatpmp = atoi(ary_options[i].value);*/
break ;
2015-06-09 13:13:25 +00:00
# endif /* ENABLE_NATPMP */
2013-07-09 13:36:53 +00:00
# ifdef ENABLE_PCP
case UPNPPCPMINLIFETIME :
min_lifetime = atoi ( ary_options [ i ] . value ) ;
if ( min_lifetime > 120 ) {
min_lifetime = 120 ;
}
break ;
case UPNPPCPMAXLIFETIME :
max_lifetime = atoi ( ary_options [ i ] . value ) ;
if ( max_lifetime > 86400 ) {
max_lifetime = 86400 ;
}
break ;
2014-05-03 04:04:45 +00:00
case UPNPPCPALLOWTHIRDPARTY :
2014-05-06 11:15:24 +00:00
if ( strcmp ( ary_options [ i ] . value , " yes " ) = = 0 )
SETFLAG ( PCP_ALLOWTHIRDPARTYMASK ) ;
2014-05-03 04:04:45 +00:00
break ;
2015-06-09 13:13:25 +00:00
# endif /* ENABLE_PCP */
2011-09-28 19:13:20 +00:00
# ifdef PF_ENABLE_FILTER_RULES
case UPNPQUICKRULES :
if ( strcmp ( ary_options [ i ] . value , " no " ) = = 0 )
SETFLAG ( PFNOQUICKRULESMASK ) ;
break ;
2015-06-09 13:13:25 +00:00
# endif /* PF_ENABLE_FILTER_RULES */
2011-09-28 19:13:20 +00:00
case UPNPENABLE :
if ( strcmp ( ary_options [ i ] . value , " yes " ) ! = 0 )
CLEARFLAG ( ENABLEUPNPMASK ) ;
break ;
case UPNPSECUREMODE :
if ( strcmp ( ary_options [ i ] . value , " yes " ) = = 0 )
SETFLAG ( SECUREMODEMASK ) ;
break ;
# ifdef ENABLE_LEASEFILE
case UPNPLEASEFILE :
lease_file = ary_options [ i ] . value ;
break ;
2015-06-09 13:13:25 +00:00
# endif /* ENABLE_LEASEFILE */
2011-09-28 19:13:20 +00:00
case UPNPMINISSDPDSOCKET :
minissdpdsocketpath = ary_options [ i ] . value ;
break ;
2018-02-20 06:14:05 +00:00
# ifdef IGD_V2
case UPNPFORCEIGDDESCV1 :
2018-02-22 12:47:48 +00:00
if ( strcmp ( ary_options [ i ] . value , " yes " ) = = 0 )
SETFLAG ( FORCEIGDDESCV1MASK ) ;
else if ( strcmp ( ary_options [ i ] . value , " no " ) ! = 0 ) {
2018-02-20 06:14:05 +00:00
fprintf ( stderr , " force_igd_desc_v1 can only be yes or no \n " ) ;
}
break ;
# endif
2011-09-28 19:13:20 +00:00
default :
fprintf ( stderr , " Unknown option in file %s \n " ,
optionsfile ) ;
}
}
2014-02-03 09:47:47 +00:00
# ifdef ENABLE_PCP
/* if lifetimes are inverse */
2013-07-09 13:36:53 +00:00
if ( min_lifetime > = max_lifetime ) {
fprintf ( stderr , " Minimum lifetime (%lu) is greater than or equal to maximum lifetime (%lu). \n " , min_lifetime , max_lifetime ) ;
fprintf ( stderr , " Check your configuration file. \n " ) ;
return 1 ;
}
2015-06-09 13:13:25 +00:00
# endif /* ENABLE_PCP */
2018-07-06 11:23:22 +00:00
if ( GETFLAG ( PERFORMSTUNMASK ) & & ! ext_stun_host ) {
2018-05-19 11:32:42 +00:00
fprintf ( stderr , " You must specify ext_stun_host= when ext_perform_stun=yes \n " ) ;
return 1 ;
}
2011-09-28 19:13:20 +00:00
}
2012-06-29 19:39:34 +00:00
# endif /* DISABLE_CONFIG_FILE */
2011-09-28 19:13:20 +00:00
/* command line arguments processing */
for ( i = 1 ; i < argc ; i + + )
{
if ( argv [ i ] [ 0 ] ! = ' - ' )
{
fprintf ( stderr , " Unknown option: %s \n " , argv [ i ] ) ;
}
else switch ( argv [ i ] [ 1 ] )
{
2020-04-09 18:41:14 +00:00
# ifdef ENABLE_IPV6
case ' 4 ' :
SETFLAG ( IPV6DISABLEDMASK ) ;
break ;
# endif
2018-02-22 13:02:52 +00:00
# ifdef IGD_V2
case ' 1 ' :
SETFLAG ( FORCEIGDDESCV1MASK ) ;
break ;
# endif
2014-12-10 09:40:41 +00:00
case ' b ' :
if ( i + 1 < argc ) {
upnp_bootid = ( unsigned int ) strtoul ( argv [ + + i ] , NULL , 10 ) ;
} else
fprintf ( stderr , " Option -%c takes one argument. \n " , argv [ i ] [ 1 ] ) ;
break ;
2011-09-28 19:13:20 +00:00
case ' o ' :
2018-07-06 11:23:22 +00:00
if ( i + 1 < argc ) {
i + + ;
if ( 0 = = strncasecmp ( argv [ i ] , " STUN: " , 5 ) ) {
2018-07-06 12:06:04 +00:00
char * ptr ;
2018-07-06 11:23:22 +00:00
SETFLAG ( PERFORMSTUNMASK ) ;
ext_stun_host = argv [ i ] + 5 ;
2018-07-06 12:06:04 +00:00
ptr = strchr ( ext_stun_host , ' : ' ) ;
if ( ptr ) {
ext_stun_port = atoi ( ptr + 1 ) ;
* ptr = 0 ;
}
2018-07-06 11:23:22 +00:00
} else
use_ext_ip_addr = argv [ i ] ;
} else
2011-09-28 19:13:20 +00:00
fprintf ( stderr , " Option -%c takes one argument. \n " , argv [ i ] [ 1 ] ) ;
break ;
case ' t ' :
if ( i + 1 < argc )
v - > notify_interval = atoi ( argv [ + + i ] ) ;
else
fprintf ( stderr , " Option -%c takes one argument. \n " , argv [ i ] [ 1 ] ) ;
break ;
2012-06-29 19:45:45 +00:00
case ' r ' :
if ( i + 1 < argc )
v - > clean_ruleset_interval = atoi ( argv [ + + i ] ) ;
else
fprintf ( stderr , " Option -%c takes one argument. \n " , argv [ i ] [ 1 ] ) ;
break ;
2011-09-28 19:13:20 +00:00
case ' u ' :
2013-06-13 14:03:36 +00:00
if ( i + 1 < argc ) {
strncpy ( uuidvalue_igd + 5 , argv [ + + i ] , strlen ( uuidvalue_igd + 5 ) + 1 ) ;
complete_uuidvalues ( ) ;
} else
2011-09-28 19:13:20 +00:00
fprintf ( stderr , " Option -%c takes one argument. \n " , argv [ i ] [ 1 ] ) ;
break ;
2013-12-13 11:03:28 +00:00
# ifdef ENABLE_MANUFACTURER_INFO_CONFIGURATION
2012-07-14 15:31:30 +00:00
case ' z ' :
if ( i + 1 < argc )
strncpy ( friendly_name , argv [ + + i ] , FRIENDLY_NAME_MAX_LEN ) ;
else
fprintf ( stderr , " Option -%c takes one argument. \n " , argv [ i ] [ 1 ] ) ;
friendly_name [ FRIENDLY_NAME_MAX_LEN - 1 ] = ' \0 ' ;
break ;
2015-06-09 13:13:25 +00:00
# endif /* ENABLE_MANUFACTURER_INFO_CONFIGURATION */
2011-09-28 19:13:20 +00:00
case ' s ' :
if ( i + 1 < argc )
strncpy ( serialnumber , argv [ + + i ] , SERIALNUMBER_MAX_LEN ) ;
else
fprintf ( stderr , " Option -%c takes one argument. \n " , argv [ i ] [ 1 ] ) ;
serialnumber [ SERIALNUMBER_MAX_LEN - 1 ] = ' \0 ' ;
break ;
case ' m ' :
if ( i + 1 < argc )
strncpy ( modelnumber , argv [ + + i ] , MODELNUMBER_MAX_LEN ) ;
else
fprintf ( stderr , " Option -%c takes one argument. \n " , argv [ i ] [ 1 ] ) ;
modelnumber [ MODELNUMBER_MAX_LEN - 1 ] = ' \0 ' ;
break ;
# ifdef ENABLE_NATPMP
case ' N ' :
/*enablenatpmp = 1;*/
SETFLAG ( ENABLENATPMPMASK ) ;
break ;
2015-06-09 13:13:25 +00:00
# endif /* ENABLE_NATPMP */
2011-09-28 19:13:20 +00:00
case ' U ' :
/*sysuptime = 1;*/
SETFLAG ( SYSUPTIMEMASK ) ;
break ;
/*case 'l':
logfilename = argv [ + + i ] ;
break ; */
2012-06-29 19:36:25 +00:00
# if defined(USE_PF) || defined(USE_IPF)
2011-09-28 19:13:20 +00:00
case ' L ' :
/*logpackets = 1;*/
SETFLAG ( LOGPACKETSMASK ) ;
break ;
2015-06-09 13:13:25 +00:00
# endif /* defined(USE_PF) || defined(USE_IPF) */
2011-09-28 19:13:20 +00:00
case ' S ' :
SETFLAG ( SECUREMODEMASK ) ;
break ;
case ' i ' :
if ( i + 1 < argc )
ext_if_name = argv [ + + i ] ;
else
fprintf ( stderr , " Option -%c takes one argument. \n " , argv [ i ] [ 1 ] ) ;
break ;
2019-05-21 08:42:40 +00:00
# ifdef ENABLE_IPV6
case ' I ' :
if ( i + 1 < argc )
ext_if_name6 = argv [ + + i ] ;
else
fprintf ( stderr , " Option -%c takes one argument. \n " , argv [ i ] [ 1 ] ) ;
break ;
# endif
2011-09-28 19:13:20 +00:00
# ifdef USE_PF
case ' q ' :
if ( i + 1 < argc )
queue = argv [ + + i ] ;
else
fprintf ( stderr , " Option -%c takes one argument. \n " , argv [ i ] [ 1 ] ) ;
break ;
case ' T ' :
if ( i + 1 < argc )
tag = argv [ + + i ] ;
else
fprintf ( stderr , " Option -%c takes one argument. \n " , argv [ i ] [ 1 ] ) ;
break ;
2015-06-09 13:13:25 +00:00
# endif /* USE_PF */
2011-09-28 19:13:20 +00:00
case ' p ' :
if ( i + 1 < argc )
v - > port = atoi ( argv [ + + i ] ) ;
else
2014-04-09 13:35:06 +00:00
fprintf ( stderr , " Option -%c takes one argument. \n " , argv [ i ] [ 1 ] ) ;
break ;
# ifdef ENABLE_HTTPS
case ' H ' :
if ( i + 1 < argc )
v - > https_port = atoi ( argv [ + + i ] ) ;
else
fprintf ( stderr , " Option -%c takes one argument. \n " , argv [ i ] [ 1 ] ) ;
break ;
2015-06-09 13:13:25 +00:00
# endif /* ENABLE_HTTPS */
2011-09-28 19:13:20 +00:00
# ifdef ENABLE_NFQUEUE
case ' Q ' :
if ( i + 1 < argc )
{
nfqueue = atoi ( argv [ + + i ] ) ;
}
else
fprintf ( stderr , " Option -%c takes one argument. \n " , argv [ i ] [ 1 ] ) ;
break ;
case ' n ' :
if ( i + 1 < argc ) {
i + + ;
if ( n_nfqix < MAX_LAN_ADDR ) {
nfqix [ n_nfqix + + ] = if_nametoindex ( argv [ i ] ) ;
} else {
fprintf ( stderr , " Too many nfq interfaces. Ignoring %s \n " , argv [ i ] ) ;
}
} else {
fprintf ( stderr , " Option -%c takes one argument. \n " , argv [ i ] [ 1 ] ) ;
}
break ;
2015-06-09 13:13:25 +00:00
# endif /* ENABLE_NFQUEUE */
2020-06-03 21:43:58 +00:00
# ifndef NO_BACKGROUND_NO_PIDFILE
2011-09-28 19:13:20 +00:00
case ' P ' :
if ( i + 1 < argc )
pidfilename = argv [ + + i ] ;
else
fprintf ( stderr , " Option -%c takes one argument. \n " , argv [ i ] [ 1 ] ) ;
break ;
2020-06-03 21:43:58 +00:00
# endif
2011-09-28 19:13:20 +00:00
case ' d ' :
debug_flag = 1 ;
break ;
case ' w ' :
if ( i + 1 < argc )
presurl = argv [ + + i ] ;
else
fprintf ( stderr , " Option -%c takes one argument. \n " , argv [ i ] [ 1 ] ) ;
break ;
case ' B ' :
if ( i + 2 < argc )
{
downstream_bitrate = strtoul ( argv [ + + i ] , 0 , 0 ) ;
upstream_bitrate = strtoul ( argv [ + + i ] , 0 , 0 ) ;
}
else
fprintf ( stderr , " Option -%c takes two arguments. \n " , argv [ i ] [ 1 ] ) ;
break ;
case ' a ' :
2012-06-27 07:16:39 +00:00
# ifndef MULTIPLE_EXTERNAL_IP
2011-09-28 19:13:20 +00:00
if ( i + 1 < argc )
{
i + + ;
lan_addr = ( struct lan_addr_s * ) malloc ( sizeof ( struct lan_addr_s ) ) ;
if ( lan_addr = = NULL )
{
fprintf ( stderr , " malloc(sizeof(struct lan_addr_s)): %m " ) ;
break ;
}
if ( parselanaddr ( lan_addr , argv [ i ] ) ! = 0 )
{
2014-05-22 07:42:05 +00:00
fprintf ( stderr , " can't parse \" %s \" as a valid "
# ifndef ENABLE_IPV6
" LAN address or "
2015-06-09 13:13:25 +00:00
# endif /* #ifndef ENABLE_IPV6 */
2014-05-22 07:42:05 +00:00
" interface name \n " , argv [ i ] ) ;
2011-09-28 19:13:20 +00:00
free ( lan_addr ) ;
break ;
}
/* check if we already have this address */
for ( lan_addr2 = lan_addrs . lh_first ; lan_addr2 ! = NULL ; lan_addr2 = lan_addr2 - > list . le_next )
{
if ( 0 = = strncmp ( lan_addr2 - > str , lan_addr - > str , 15 ) )
break ;
}
if ( lan_addr2 = = NULL )
LIST_INSERT_HEAD ( & lan_addrs , lan_addr , list ) ;
}
else
fprintf ( stderr , " Option -%c takes one argument. \n " , argv [ i ] [ 1 ] ) ;
2015-06-09 13:13:25 +00:00
# else /* #ifndef MULTIPLE_EXTERNAL_IP */
2012-06-27 07:16:39 +00:00
if ( i + 2 < argc )
{
2015-12-30 23:41:24 +00:00
char * val = calloc ( ( strlen ( argv [ i + 1 ] ) + strlen ( argv [ i + 2 ] ) + 2 ) , sizeof ( char ) ) ;
2012-06-27 07:16:39 +00:00
if ( val = = NULL )
{
fprintf ( stderr , " memory allocation error for listen address storage \n " ) ;
break ;
}
sprintf ( val , " %s %s " , argv [ i + 1 ] , argv [ i + 2 ] ) ;
lan_addr = ( struct lan_addr_s * ) malloc ( sizeof ( struct lan_addr_s ) ) ;
if ( lan_addr = = NULL )
{
fprintf ( stderr , " malloc(sizeof(struct lan_addr_s)): %m " ) ;
free ( val ) ;
break ;
}
if ( parselanaddr ( lan_addr , val ) ! = 0 )
{
2014-05-22 07:42:05 +00:00
fprintf ( stderr , " can't parse \" %s \" as a valid LAN address or interface name \n " , val ) ;
2012-06-27 07:16:39 +00:00
free ( lan_addr ) ;
free ( val ) ;
break ;
}
/* check if we already have this address */
for ( lan_addr2 = lan_addrs . lh_first ; lan_addr2 ! = NULL ; lan_addr2 = lan_addr2 - > list . le_next )
{
if ( 0 = = strncmp ( lan_addr2 - > str , lan_addr - > str , 15 ) )
break ;
}
if ( lan_addr2 = = NULL )
LIST_INSERT_HEAD ( & lan_addrs , lan_addr , list ) ;
free ( val ) ;
i + = 2 ;
}
else
fprintf ( stderr , " Option -%c takes two arguments. \n " , argv [ i ] [ 1 ] ) ;
2015-06-09 13:13:25 +00:00
# endif /* #ifndef MULTIPLE_EXTERNAL_IP */
2011-09-28 19:13:20 +00:00
break ;
2012-07-17 19:39:40 +00:00
case ' A ' :
if ( i + 1 < argc ) {
void * tmp ;
tmp = realloc ( upnppermlist , sizeof ( struct upnpperm ) * ( num_upnpperm + 1 ) ) ;
if ( tmp = = NULL ) {
fprintf ( stderr , " memory allocation error for permission \n " ) ;
} else {
upnppermlist = tmp ;
if ( read_permission_line ( upnppermlist + num_upnpperm , argv [ + + i ] ) > = 0 ) {
num_upnpperm + + ;
} else {
fprintf ( stderr , " Permission rule parsing error : \n %s \n " , argv [ i ] ) ;
}
}
} else
fprintf ( stderr , " Option -%c takes one argument. \n " , argv [ i ] [ 1 ] ) ;
break ;
2011-09-28 19:13:20 +00:00
case ' f ' :
i + + ; /* discarding, the config file is already read */
break ;
default :
fprintf ( stderr , " Unknown option: %s \n " , argv [ i ] ) ;
}
}
2019-05-21 08:42:40 +00:00
if ( ! ext_if_name | | ! lan_addrs . lh_first ) {
2011-09-28 19:13:20 +00:00
/* bad configuration */
goto print_usage ;
}
2019-05-21 08:42:40 +00:00
/* IPv6 ifname is defaulted to same as IPv4 */
# ifdef ENABLE_IPV6
if ( ! ext_if_name6 )
ext_if_name6 = ext_if_name ;
# endif
2018-07-06 11:23:22 +00:00
if ( use_ext_ip_addr & & GETFLAG ( PERFORMSTUNMASK ) ) {
2018-05-19 11:32:42 +00:00
fprintf ( stderr , " Error: options ext_ip= and ext_perform_stun=yes cannot be specified together \n " ) ;
return 1 ;
}
2018-05-19 11:31:26 +00:00
if ( use_ext_ip_addr ) {
if ( inet_pton ( AF_INET , use_ext_ip_addr , & addr ) ! = 1 ) {
fprintf ( stderr , " Error: option ext_ip contains invalid address %s \n " , use_ext_ip_addr ) ;
return 1 ;
}
if ( addr_is_reserved ( & addr ) ) {
fprintf ( stderr , " Error: option ext_ip contains reserved / private address %s, not public routable \n " , use_ext_ip_addr ) ;
return 1 ;
}
}
2020-06-03 21:43:58 +00:00
# ifndef NO_BACKGROUND_NO_PIDFILE
2011-09-28 19:13:20 +00:00
if ( debug_flag )
{
pid = getpid ( ) ;
}
else
{
# ifdef USE_DAEMON
if ( daemon ( 0 , 0 ) < 0 ) {
perror ( " daemon() " ) ;
}
pid = getpid ( ) ;
# else
pid = daemonize ( ) ;
# endif
}
2020-06-03 21:43:58 +00:00
# endif
2011-09-28 19:13:20 +00:00
openlog_option = LOG_PID | LOG_CONS ;
if ( debug_flag )
{
openlog_option | = LOG_PERROR ; /* also log on stderr */
}
openlog ( " miniupnpd " , openlog_option , LOG_MINIUPNPD ) ;
if ( ! debug_flag )
{
/* speed things up and ignore LOG_INFO and LOG_DEBUG */
setlogmask ( LOG_UPTO ( LOG_NOTICE ) ) ;
}
2020-06-03 21:43:58 +00:00
# ifndef NO_BACKGROUND_NO_PIDFILE
2011-09-28 19:13:20 +00:00
if ( checkforrunning ( pidfilename ) < 0 )
{
syslog ( LOG_ERR , " MiniUPnPd is already running. EXITING " ) ;
return 1 ;
2012-03-01 01:44:38 +00:00
}
2020-06-03 21:43:58 +00:00
# endif
2016-01-04 03:56:39 +00:00
2015-06-06 03:19:52 +00:00
# ifdef TOMATO
syslog ( LOG_NOTICE , " version " MINIUPNPD_VERSION " started " ) ;
2015-06-09 13:13:25 +00:00
# endif /* TOMATO */
2011-09-28 19:13:20 +00:00
2018-03-13 10:43:07 +00:00
set_startup_time ( ) ;
2011-09-28 19:13:20 +00:00
/* presentation url */
if ( presurl )
{
strncpy ( presentationurl , presurl , PRESENTATIONURL_MAX_LEN ) ;
presentationurl [ PRESENTATIONURL_MAX_LEN - 1 ] = ' \0 ' ;
}
else
{
snprintf ( presentationurl , PRESENTATIONURL_MAX_LEN ,
" http://%s/ " , lan_addrs . lh_first - > str ) ;
/*"http://%s:%d/", lan_addrs.lh_first->str, 80);*/
}
/* set signal handler */
memset ( & sa , 0 , sizeof ( struct sigaction ) ) ;
sa . sa_handler = sigterm ;
2012-05-24 16:17:31 +00:00
if ( sigaction ( SIGTERM , & sa , NULL ) < 0 )
2011-09-28 19:13:20 +00:00
{
syslog ( LOG_ERR , " Failed to set %s handler. EXITING " , " SIGTERM " ) ;
return 1 ;
}
2012-05-24 16:17:31 +00:00
if ( sigaction ( SIGINT , & sa , NULL ) < 0 )
2011-09-28 19:13:20 +00:00
{
syslog ( LOG_ERR , " Failed to set %s handler. EXITING " , " SIGINT " ) ;
return 1 ;
}
2015-06-06 03:19:52 +00:00
# ifdef TOMATO
sa . sa_handler = sigusr2 ;
sigaction ( SIGUSR2 , & sa , NULL ) ;
if ( signal ( SIGPIPE , SIG_IGN ) = = SIG_ERR )
2015-06-09 13:13:25 +00:00
# else /* TOMATO */
2012-05-24 16:17:31 +00:00
sa . sa_handler = SIG_IGN ;
if ( sigaction ( SIGPIPE , & sa , NULL ) < 0 )
2015-06-09 13:13:25 +00:00
# endif /* TOMATO */
2012-05-24 16:17:31 +00:00
{
2011-09-28 19:13:20 +00:00
syslog ( LOG_ERR , " Failed to ignore SIGPIPE signals " ) ;
}
sa . sa_handler = sigusr1 ;
2012-05-24 16:17:31 +00:00
if ( sigaction ( SIGUSR1 , & sa , NULL ) < 0 )
2011-09-28 19:13:20 +00:00
{
syslog ( LOG_NOTICE , " Failed to set %s handler " , " SIGUSR1 " ) ;
}
2018-05-02 07:40:12 +00:00
# if !defined(TOMATO) && defined(ENABLE_LEASEFILE) && defined(LEASEFILE_USE_REMAINING_TIME)
sa . sa_handler = sigusr2 ;
if ( sigaction ( SIGUSR2 , & sa , NULL ) < 0 )
{
syslog ( LOG_NOTICE , " Failed to set %s handler " , " SIGUSR2 " ) ;
}
# endif /* !TOMATO && ENABLE_LEASEFILE && LEASEFILE_USE_REMAINING_TIME */
2011-09-28 19:13:20 +00:00
2014-02-25 09:24:22 +00:00
/* initialize random number generator */
srandom ( ( unsigned int ) time ( NULL ) ) ;
2015-09-14 10:10:15 +00:00
# ifdef RANDOMIZE_URLS
snprintf ( random_url , RANDOM_URL_MAX_LEN , " %08lx " , random ( ) ) ;
# endif /* RANDOMIZE_URLS */
2014-02-25 09:24:22 +00:00
2014-02-25 09:23:59 +00:00
/* initialize redirection engine (and pinholes) */
2011-09-28 19:13:20 +00:00
if ( init_redirect ( ) < 0 )
{
syslog ( LOG_ERR , " Failed to init redirection engine. EXITING " ) ;
return 1 ;
}
2014-05-10 11:09:48 +00:00
# ifdef ENABLE_UPNPPINHOLE
2012-04-27 06:51:25 +00:00
# ifdef USE_NETFILTER
init_iptpinhole ( ) ;
# endif
# endif
2011-09-28 19:13:20 +00:00
2020-06-03 21:43:58 +00:00
# ifndef NO_BACKGROUND_NO_PIDFILE
2012-03-31 07:22:50 +00:00
if ( writepidfile ( pidfilename , pid ) < 0 )
pidfilename = NULL ;
2020-06-03 21:43:58 +00:00
# endif
2011-09-28 19:13:20 +00:00
# ifdef ENABLE_LEASEFILE
/*remove(lease_file);*/
syslog ( LOG_INFO , " Reloading rules from lease file " ) ;
reload_from_lease_file ( ) ;
# endif
2015-06-06 03:19:52 +00:00
# ifdef TOMATO
tomato_load ( ) ;
2015-06-09 13:13:25 +00:00
# endif /* TOMATO */
2015-06-06 03:19:52 +00:00
2011-09-28 19:13:20 +00:00
return 0 ;
print_usage :
fprintf ( stderr , " Usage: \n \t "
2019-10-05 19:59:42 +00:00
" %s --version \n \t "
2012-06-29 19:39:34 +00:00
" %s "
# ifndef DISABLE_CONFIG_FILE
" [-f config_file] "
# endif
2019-05-21 08:42:40 +00:00
" [-i ext_ifname] "
# ifdef ENABLE_IPV6
2020-04-09 18:41:14 +00:00
" [-I ext_ifname6] [-4] "
2019-05-21 08:42:40 +00:00
# endif
" [-o ext_ip] \n "
2012-06-27 07:16:39 +00:00
# ifndef MULTIPLE_EXTERNAL_IP
" \t \t [-a listening_ip] "
# else
" \t \t [-a listening_ip ext_ip] "
2014-04-09 13:35:06 +00:00
# endif
# ifdef ENABLE_HTTPS
" [-H https_port] "
2012-06-27 07:16:39 +00:00
# endif
" [-p port] [-d] "
2012-06-29 19:36:25 +00:00
# if defined(USE_PF) || defined(USE_IPF)
2012-06-27 07:19:44 +00:00
" [-L] "
2011-09-28 19:13:20 +00:00
# endif
2012-06-29 19:47:25 +00:00
" [-U] [-S] "
2012-06-27 07:19:44 +00:00
# ifdef ENABLE_NATPMP
" [-N] "
# endif
" \n "
2011-09-28 19:13:20 +00:00
/*"[-l logfile] " not functionnal */
" \t \t [-u uuid] [-s serial] [-m model_number] \n "
2020-06-03 21:43:58 +00:00
" \t \t [-t notify_interval] "
# ifndef NO_BACKGROUND_NO_PIDFILE
" [-P pid_filename] "
# endif
2013-12-13 11:03:28 +00:00
# ifdef ENABLE_MANUFACTURER_INFO_CONFIGURATION
2014-12-10 09:44:32 +00:00
" [-z fiendly_name] "
2013-12-13 11:03:28 +00:00
# endif
2014-12-10 09:44:32 +00:00
" \n \t \t [-B down up] [-w url] [-r clean_ruleset_interval] \n "
2011-09-28 19:13:20 +00:00
# ifdef USE_PF
" \t \t [-q queue] [-T tag] \n "
# endif
# ifdef ENABLE_NFQUEUE
" \t \t [-Q queue] [-n name] \n "
# endif
2018-02-22 13:02:52 +00:00
" \t \t [-A \" permission rule \" ] [-b BOOTID] "
# ifdef IGD_V2
" [-1] "
# endif
" \n "
2011-09-28 19:13:20 +00:00
" \n Notes: \n \t There can be one or several listening_ips. \n "
" \t Notify interval is in seconds. Default is 30 seconds. \n "
2020-06-03 21:43:58 +00:00
# ifndef NO_BACKGROUND_NO_PIDFILE
2011-09-28 19:13:20 +00:00
" \t Default pid file is '%s'. \n "
2020-06-03 21:43:58 +00:00
# endif
2011-09-28 19:13:20 +00:00
" \t Default config file is '%s'. \n "
" \t With -d miniupnpd will run as a standard program. \n "
2018-07-06 12:06:04 +00:00
" \t -o argument is either an IPv4 address or \" STUN:host[:port] \" . \n "
2020-04-09 18:41:14 +00:00
# ifdef ENABLE_IPV6
" \t -4 disable IPv6 \n "
# endif
2012-06-29 19:36:25 +00:00
# if defined(USE_PF) || defined(USE_IPF)
2011-09-28 19:13:20 +00:00
" \t -L sets packet log in pf and ipf on. \n "
2012-06-27 07:19:44 +00:00
# endif
2011-09-28 19:13:20 +00:00
" \t -S sets \" secure \" mode : clients can only add mappings to their own ip \n "
" \t -U causes miniupnpd to report system uptime instead "
" of daemon uptime. \n "
# ifdef ENABLE_NATPMP
2014-12-10 09:44:32 +00:00
" \t -N enables NAT-PMP functionality. \n "
2011-09-28 19:13:20 +00:00
# endif
" \t -B sets bitrates reported by daemon in bits per second. \n "
" \t -w sets the presentation url. Default is http address on port 80 \n "
# ifdef USE_PF
" \t -q sets the ALTQ queue in pf. \n "
" \t -T sets the tag name in pf. \n "
# endif
# ifdef ENABLE_NFQUEUE
2012-07-17 19:39:05 +00:00
" \t -Q sets the queue number that is used by NFQUEUE. \n "
" \t -n sets the name of the interface(s) that packets will arrive on. \n "
2011-09-28 19:13:20 +00:00
# endif
2012-07-17 19:39:40 +00:00
" \t -A use following syntax for permission rules : \n "
" \t (allow|deny) (external port range) ip/mask (internal port range) \n "
" \t examples : \n "
" \t \" allow 1024-65535 192.168.1.0/24 1024-65535 \" \n "
" \t \" deny 0-65535 0.0.0.0/0 0-65535 \" \n "
2014-12-10 09:40:41 +00:00
" \t -b sets the value of BOOTID.UPNP.ORG SSDP header \n "
2018-02-22 13:02:52 +00:00
# ifdef IGD_V2
" \t -1 force reporting IGDv1 in rootDesc *use with care* \n "
# endif
2011-09-28 19:13:20 +00:00
" \t -h prints this help and quits. \n "
2020-06-03 21:43:58 +00:00
" " , argv [ 0 ] , argv [ 0 ] ,
# ifndef NO_BACKGROUND_NO_PIDFILE
pidfilename ,
# endif
DEFAULT_CONFIG ) ;
2011-09-28 19:13:20 +00:00
return 1 ;
}
/* === main === */
/* process HTTP or SSDP requests */
int
main ( int argc , char * * argv )
{
int i ;
int shttpl = - 1 ; /* socket for HTTP */
2014-03-13 10:30:24 +00:00
# if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6)
int shttpl_v4 = - 1 ; /* socket for HTTP (ipv4 only) */
# endif
2014-04-09 13:35:06 +00:00
# ifdef ENABLE_HTTPS
int shttpsl = - 1 ; /* socket for HTTPS */
# if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6)
int shttpsl_v4 = - 1 ; /* socket for HTTPS (ipv4 only) */
# endif
# endif /* ENABLE_HTTPS */
2011-09-28 19:13:20 +00:00
int sudp = - 1 ; /* IP v4 socket for receiving SSDP */
# ifdef ENABLE_IPV6
int sudpv6 = - 1 ; /* IP v6 socket for receiving SSDP */
# endif
# ifdef ENABLE_NATPMP
2014-02-06 09:55:28 +00:00
int * snatpmp = NULL ; /* also used for PCP */
2011-09-28 19:13:20 +00:00
# endif
2014-03-24 11:07:31 +00:00
# if defined(ENABLE_IPV6) && defined(ENABLE_PCP)
int spcp_v6 = - 1 ;
# endif
2011-09-28 19:13:20 +00:00
# ifdef ENABLE_NFQUEUE
int nfqh = - 1 ;
# endif
# ifdef USE_IFACEWATCHER
int sifacewatcher = - 1 ;
# endif
2012-02-04 23:40:50 +00:00
int * snotify = NULL ;
2011-09-28 19:13:20 +00:00
int addr_count ;
LIST_HEAD ( httplisthead , upnphttp ) upnphttphead ;
struct upnphttp * e = 0 ;
struct upnphttp * next ;
fd_set readset ; /* for select() */
fd_set writeset ;
struct timeval timeout , timeofday , lasttimeofday = { 0 , 0 } ;
int max_fd = - 1 ;
# ifdef USE_MINIUPNPDCTL
int sctl = - 1 ;
LIST_HEAD ( ctlstructhead , ctlelem ) ctllisthead ;
struct ctlelem * ectl ;
struct ctlelem * ectlnext ;
# endif
struct runtime_vars v ;
/* variables used for the unused-rule cleanup process */
struct rule_state * rule_list = 0 ;
struct timeval checktime = { 0 , 0 } ;
struct lan_addr_s * lan_addr ;
2014-05-10 11:09:48 +00:00
# ifdef ENABLE_UPNPPINHOLE
2012-05-08 20:51:23 +00:00
unsigned int next_pinhole_ts ;
# endif
2011-09-28 19:13:20 +00:00
2019-10-05 19:59:42 +00:00
for ( i = 0 ; i < argc ; i + + ) {
if ( strcmp ( argv [ i ] , " version " ) = = 0 | | strcmp ( argv [ i ] , " --version " ) = = 0 ) {
puts ( " miniupnpd " MINIUPNPD_VERSION
# ifdef MINIUPNPD_GIT_REF
" " MINIUPNPD_GIT_REF
# endif
" " __DATE__ ) ;
2019-10-13 20:16:45 +00:00
# ifdef USE_PF
puts ( " using pf backend " ) ;
# endif
# ifdef USE_IPF
puts ( " using ipf backend " ) ;
# endif
# ifdef USE_IPFW
puts ( " using ipfw backend " ) ;
# endif
# ifdef USE_IPTABLES
puts ( " using netfilter(iptables) backend " ) ;
# endif
# ifdef USE_NFTABLES
puts ( " using netfilter(nftables) backend " ) ;
# endif
2019-10-05 19:59:42 +00:00
# ifdef ENABLE_HTTPS
# ifdef OPENSSL_VERSION
puts ( OpenSSL_version ( OPENSSL_VERSION ) ) ;
# else
puts ( SSLeay_version ( SSLEAY_VERSION ) ) ;
# endif
# endif
return 0 ;
}
}
2011-09-28 19:13:20 +00:00
if ( init ( argc , argv , & v ) ! = 0 )
return 1 ;
2014-04-09 13:35:06 +00:00
# ifdef ENABLE_HTTPS
if ( init_ssl ( ) < 0 )
return 1 ;
# endif /* ENABLE_HTTPS */
2011-09-28 19:13:20 +00:00
/* count lan addrs */
addr_count = 0 ;
for ( lan_addr = lan_addrs . lh_first ; lan_addr ! = NULL ; lan_addr = lan_addr - > list . le_next )
addr_count + + ;
2012-02-04 23:40:50 +00:00
if ( addr_count > 0 ) {
2012-04-06 17:30:19 +00:00
# ifndef ENABLE_IPV6
2012-02-04 23:40:50 +00:00
snotify = calloc ( addr_count , sizeof ( int ) ) ;
2012-04-06 17:30:19 +00:00
# else
/* one for IPv4, one for IPv6 */
snotify = calloc ( addr_count * 2 , sizeof ( int ) ) ;
# endif
2012-02-04 23:40:50 +00:00
}
2011-09-28 19:13:20 +00:00
# ifdef ENABLE_NATPMP
2012-02-04 23:40:50 +00:00
if ( addr_count > 0 ) {
snatpmp = malloc ( addr_count * sizeof ( int ) ) ;
for ( i = 0 ; i < addr_count ; i + + )
snatpmp [ i ] = - 1 ;
}
2011-09-28 19:13:20 +00:00
# endif
LIST_INIT ( & upnphttphead ) ;
# ifdef USE_MINIUPNPDCTL
LIST_INIT ( & ctllisthead ) ;
# endif
if (
# ifdef ENABLE_NATPMP
2016-12-28 10:58:21 +00:00
! GETFLAG ( ENABLENATPMPMASK ) & & ! GETFLAG ( ENABLEUPNPMASK )
# else
! GETFLAG ( ENABLEUPNPMASK )
2011-09-28 19:13:20 +00:00
# endif
2016-12-28 10:58:21 +00:00
) {
2011-09-28 19:13:20 +00:00
syslog ( LOG_ERR , " Why did you run me anyway? " ) ;
return 0 ;
}
2015-01-20 13:13:18 +00:00
syslog ( LOG_INFO , " version " MINIUPNPD_VERSION " starting%s%sext if %s BOOTID=%u " ,
2012-09-15 15:36:10 +00:00
# ifdef ENABLE_NATPMP
2013-07-09 13:36:53 +00:00
# ifdef ENABLE_PCP
GETFLAG ( ENABLENATPMPMASK ) ? " NAT-PMP/PCP " : " " ,
# else
2012-09-15 15:36:10 +00:00
GETFLAG ( ENABLENATPMPMASK ) ? " NAT-PMP " : " " ,
2013-07-09 13:36:53 +00:00
# endif
2012-09-15 15:36:10 +00:00
# else
" " ,
# endif
GETFLAG ( ENABLEUPNPMASK ) ? " UPnP-IGD " : " " ,
2014-12-10 09:40:41 +00:00
ext_if_name , upnp_bootid ) ;
2019-05-21 08:42:40 +00:00
# ifdef ENABLE_IPV6
if ( ext_if_name6 ! = ext_if_name ) {
syslog ( LOG_INFO , " specific IPv6 ext if %s " , ext_if_name6 ) ;
}
# endif
2012-09-15 15:36:10 +00:00
2018-07-06 11:23:22 +00:00
if ( GETFLAG ( PERFORMSTUNMASK ) )
2018-05-19 11:32:42 +00:00
{
2018-07-06 11:29:33 +00:00
if ( update_ext_ip_addr_from_stun ( 1 ) ! = 0 ) {
2018-05-19 11:32:42 +00:00
syslog ( LOG_ERR , " Performing STUN failed. EXITING " ) ;
return 1 ;
}
}
else if ( ! use_ext_ip_addr )
2018-05-19 11:32:04 +00:00
{
char if_addr [ INET_ADDRSTRLEN ] ;
struct in_addr addr ;
if ( getifaddr ( ext_if_name , if_addr , INET_ADDRSTRLEN , & addr , NULL ) < 0 ) {
2018-07-06 11:29:33 +00:00
syslog ( LOG_WARNING , " Cannot get IP address for ext interface %s. Network is down " , ext_if_name ) ;
} else if ( addr_is_reserved ( & addr ) ) {
2018-05-19 11:32:04 +00:00
syslog ( LOG_INFO , " Reserved / private IP address %s on ext interface %s: Port forwarding is impossible " , if_addr , ext_if_name ) ;
2018-05-19 11:32:42 +00:00
syslog ( LOG_INFO , " You are probably behind NAT, enable option ext_perform_stun=yes to detect public IP address " ) ;
2020-04-12 17:44:27 +00:00
syslog ( LOG_INFO , " Or use ext_ip= / -o option to declare public IP address " ) ;
2018-05-19 11:32:04 +00:00
disable_port_forwarding = 1 ;
}
}
2011-09-28 19:13:20 +00:00
if ( GETFLAG ( ENABLEUPNPMASK ) )
{
2014-05-21 23:38:18 +00:00
unsigned short listen_port ;
listen_port = ( v . port > 0 ) ? v . port : 0 ;
2011-09-28 19:13:20 +00:00
/* open socket for HTTP connections. Listen on the 1st LAN address */
2014-03-13 10:30:24 +00:00
# ifdef ENABLE_IPV6
2020-04-09 18:41:14 +00:00
shttpl = OpenAndConfHTTPSocket ( & listen_port , ! GETFLAG ( IPV6DISABLEDMASK ) ) ;
2014-03-13 10:30:24 +00:00
# else /* ENABLE_IPV6 */
2014-05-21 23:38:18 +00:00
shttpl = OpenAndConfHTTPSocket ( & listen_port ) ;
2014-03-13 10:30:24 +00:00
# endif /* ENABLE_IPV6 */
2011-09-28 19:13:20 +00:00
if ( shttpl < 0 )
{
syslog ( LOG_ERR , " Failed to open socket for HTTP. EXITING " ) ;
return 1 ;
}
2014-05-21 23:38:18 +00:00
v . port = listen_port ;
2011-09-28 19:13:20 +00:00
syslog ( LOG_NOTICE , " HTTP listening on port %d " , v . port ) ;
2014-03-13 10:30:24 +00:00
# if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6)
2014-05-21 23:38:18 +00:00
if ( ! GETFLAG ( IPV6DISABLEDMASK ) )
2014-03-13 10:30:24 +00:00
{
2014-05-21 23:38:18 +00:00
shttpl_v4 = OpenAndConfHTTPSocket ( & listen_port , 0 ) ;
if ( shttpl_v4 < 0 )
{
2016-01-04 03:56:39 +00:00
syslog ( LOG_ERR , " Failed to open socket for HTTP on port %d (IPv4). EXITING " , v . port ) ;
2014-05-21 23:38:18 +00:00
return 1 ;
}
2014-03-13 10:30:24 +00:00
}
# endif /* V6SOCKETS_ARE_V6ONLY */
2014-04-09 13:35:06 +00:00
# ifdef ENABLE_HTTPS
/* https */
2014-05-21 23:38:18 +00:00
listen_port = ( v . https_port > 0 ) ? v . https_port : 0 ;
2014-04-09 13:35:06 +00:00
# ifdef ENABLE_IPV6
2020-04-09 18:41:14 +00:00
shttpsl = OpenAndConfHTTPSocket ( & listen_port , ! GETFLAG ( IPV6DISABLEDMASK ) ) ;
2014-04-09 13:35:06 +00:00
# else /* ENABLE_IPV6 */
2014-05-21 23:38:18 +00:00
shttpsl = OpenAndConfHTTPSocket ( & listen_port ) ;
2014-04-09 13:35:06 +00:00
# endif /* ENABLE_IPV6 */
2019-01-23 08:25:10 +00:00
if ( shttpsl < 0 )
2014-04-09 13:35:06 +00:00
{
syslog ( LOG_ERR , " Failed to open socket for HTTPS. EXITING " ) ;
return 1 ;
}
2014-05-21 23:38:18 +00:00
v . https_port = listen_port ;
2014-04-09 13:35:06 +00:00
syslog ( LOG_NOTICE , " HTTPS listening on port %d " , v . https_port ) ;
# if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6)
2014-05-21 23:38:18 +00:00
shttpsl_v4 = OpenAndConfHTTPSocket ( & listen_port , 0 ) ;
2014-04-09 13:35:06 +00:00
if ( shttpsl_v4 < 0 )
{
2016-01-04 03:56:39 +00:00
syslog ( LOG_ERR , " Failed to open socket for HTTPS on port %d (IPv4). EXITING " , v . https_port ) ;
2014-04-09 13:35:06 +00:00
return 1 ;
}
# endif /* V6SOCKETS_ARE_V6ONLY */
# endif /* ENABLE_HTTPS */
2011-09-28 19:13:20 +00:00
# ifdef ENABLE_IPV6
2016-12-16 08:39:19 +00:00
if ( ! GETFLAG ( IPV6DISABLEDMASK ) ) {
if ( find_ipv6_addr ( lan_addrs . lh_first ? lan_addrs . lh_first - > ifname : NULL ,
ipv6_addr_for_http_with_brackets , sizeof ( ipv6_addr_for_http_with_brackets ) ) > 0 ) {
syslog ( LOG_NOTICE , " HTTP IPv6 address given to control points : %s " ,
ipv6_addr_for_http_with_brackets ) ;
} else {
memcpy ( ipv6_addr_for_http_with_brackets , " [::1] " , 6 ) ;
syslog ( LOG_WARNING , " no HTTP IPv6 address, disabling IPv6 " ) ;
SETFLAG ( IPV6DISABLEDMASK ) ;
}
2011-09-28 19:13:20 +00:00
}
2016-12-16 08:39:19 +00:00
# endif /* ENABLE_IPV6 */
2011-09-28 19:13:20 +00:00
/* open socket for SSDP connections */
sudp = OpenAndConfSSDPReceiveSocket ( 0 ) ;
if ( sudp < 0 )
{
2012-09-15 15:36:10 +00:00
syslog ( LOG_NOTICE , " Failed to open socket for receiving SSDP. Trying to use MiniSSDPd " ) ;
2011-09-28 19:13:20 +00:00
if ( SubmitServicesToMiniSSDPD ( lan_addrs . lh_first - > str , v . port ) < 0 ) {
syslog ( LOG_ERR , " Failed to connect to MiniSSDPd. EXITING " ) ;
return 1 ;
}
}
# ifdef ENABLE_IPV6
2014-03-13 10:26:53 +00:00
if ( ! GETFLAG ( IPV6DISABLEDMASK ) )
2011-09-28 19:13:20 +00:00
{
2014-02-06 09:55:28 +00:00
sudpv6 = OpenAndConfSSDPReceiveSocket ( 1 ) ;
if ( sudpv6 < 0 )
{
syslog ( LOG_WARNING , " Failed to open socket for receiving SSDP (IP v6). " ) ;
}
2011-09-28 19:13:20 +00:00
}
# endif
/* open socket for sending notifications */
if ( OpenAndConfSSDPNotifySockets ( snotify ) < 0 )
{
syslog ( LOG_ERR , " Failed to open sockets for sending SSDP notify "
" messages. EXITING " ) ;
return 1 ;
}
# ifdef USE_IFACEWATCHER
/* open socket for kernel notifications about new network interfaces */
if ( sudp > = 0 )
{
sifacewatcher = OpenAndConfInterfaceWatchSocket ( ) ;
if ( sifacewatcher < 0 )
{
syslog ( LOG_ERR , " Failed to open socket for receiving network interface notifications " ) ;
}
}
# endif
}
# ifdef ENABLE_NATPMP
/* open socket for NAT PMP traffic */
if ( GETFLAG ( ENABLENATPMPMASK ) )
{
if ( OpenAndConfNATPMPSockets ( snatpmp ) < 0 )
2013-07-09 13:36:53 +00:00
# ifdef ENABLE_PCP
{
syslog ( LOG_ERR , " Failed to open sockets for NAT-PMP/PCP. " ) ;
} else {
syslog ( LOG_NOTICE , " Listening for NAT-PMP/PCP traffic on port %u " ,
NATPMP_PORT ) ;
}
# else
2011-09-28 19:13:20 +00:00
{
syslog ( LOG_ERR , " Failed to open sockets for NAT PMP. " ) ;
} else {
syslog ( LOG_NOTICE , " Listening for NAT-PMP traffic on port %u " ,
NATPMP_PORT ) ;
}
# endif
}
# endif
2014-03-24 11:07:31 +00:00
# if defined(ENABLE_IPV6) && defined(ENABLE_PCP)
2016-12-16 08:32:41 +00:00
if ( ! GETFLAG ( IPV6DISABLEDMASK ) ) {
spcp_v6 = OpenAndConfPCPv6Socket ( ) ;
}
2014-03-24 11:07:31 +00:00
# endif
2011-09-28 19:13:20 +00:00
/* for miniupnpdctl */
# ifdef USE_MINIUPNPDCTL
sctl = OpenAndConfCtlUnixSocket ( " /var/run/miniupnpd.ctl " ) ;
# endif
# ifdef ENABLE_NFQUEUE
if ( nfqueue ! = - 1 & & n_nfqix > 0 ) {
nfqh = OpenAndConfNFqueue ( ) ;
if ( nfqh < 0 ) {
syslog ( LOG_ERR , " Failed to open fd for NFQUEUE. " ) ;
return 1 ;
} else {
syslog ( LOG_NOTICE , " Opened NFQUEUE %d " , nfqueue ) ;
}
}
# endif
2012-05-24 16:22:31 +00:00
2015-06-06 03:19:52 +00:00
# ifdef TOMATO
tomato_helper ( ) ;
# endif
2018-05-08 21:06:19 +00:00
# ifdef ENABLE_PCP
if ( GETFLAG ( ENABLENATPMPMASK ) )
{
/* Send PCP startup announcements */
# ifdef ENABLE_IPV6
PCPSendUnsolicitedAnnounce ( snatpmp , addr_count , spcp_v6 ) ;
# else /* IPv4 only */
PCPSendUnsolicitedAnnounce ( snatpmp , addr_count ) ;
# endif
}
# endif
2019-10-03 21:23:53 +00:00
/* drop privileges */
# ifdef HAS_PLEDGE
/* mcast ? unix ? */
if ( pledge ( " stdio inet pf " , NULL ) < 0 ) {
syslog ( LOG_ERR , " pledge(): %m " ) ;
return 1 ;
}
# endif /* HAS_PLEDGE */
2020-04-13 01:39:01 +00:00
# ifdef HAS_LIBCAP
{
cap_t caps = cap_get_proc ( ) ;
if ( caps = = NULL ) {
syslog ( LOG_ERR , " cap_get_proc(): %m " ) ;
} else {
static const cap_value_t cap_list [ 3 ] = { CAP_NET_BROADCAST , CAP_NET_ADMIN , CAP_NET_RAW } ;
char * txt_caps = cap_to_text ( caps , NULL ) ;
if ( txt_caps = = NULL ) {
syslog ( LOG_ERR , " cap_to_text(): %m " ) ;
} else {
syslog ( LOG_DEBUG , " capabilities %s " , txt_caps ) ;
if ( cap_free ( txt_caps ) < 0 ) {
syslog ( LOG_ERR , " cap_free(): %m " ) ;
}
}
if ( cap_clear ( caps ) < 0 ) {
syslog ( LOG_ERR , " cap_clear(): %m " ) ;
}
if ( cap_set_flag ( caps , CAP_PERMITTED , sizeof ( cap_list ) / sizeof ( cap_list [ 0 ] ) , cap_list , CAP_SET ) < 0 ) {
syslog ( LOG_ERR , " cap_set_flag(): %m " ) ;
}
if ( cap_set_flag ( caps , CAP_EFFECTIVE , sizeof ( cap_list ) / sizeof ( cap_list [ 0 ] ) , cap_list , CAP_SET ) < 0 ) {
syslog ( LOG_ERR , " cap_set_flag(): %m " ) ;
}
txt_caps = cap_to_text ( caps , NULL ) ;
if ( txt_caps = = NULL ) {
syslog ( LOG_ERR , " cap_to_text(): %m " ) ;
} else {
syslog ( LOG_DEBUG , " capabilities %s " , txt_caps ) ;
if ( cap_free ( txt_caps ) < 0 ) {
syslog ( LOG_ERR , " cap_free(): %m " ) ;
}
}
if ( cap_set_proc ( caps ) < 0 ) {
syslog ( LOG_ERR , " cap_set_proc(): %m " ) ;
}
if ( cap_free ( caps ) < 0 ) {
syslog ( LOG_ERR , " cap_free(): %m " ) ;
}
}
}
# endif /* HAS_LIBCAP */
2020-05-06 22:10:49 +00:00
# ifdef HAS_LIBCAP_NG
capng_setpid ( getpid ( ) ) ;
capng_clear ( CAPNG_SELECT_BOTH ) ;
if ( capng_updatev ( CAPNG_ADD , CAPNG_EFFECTIVE | CAPNG_PERMITTED , CAP_NET_BROADCAST , CAP_NET_ADMIN , CAP_NET_RAW , - 1 ) < 0 ) {
syslog ( LOG_ERR , " capng_updatev() failed " ) ;
} else {
if ( capng_apply ( CAPNG_SELECT_BOTH ) < 0 ) {
syslog ( LOG_ERR , " capng_apply() failed " ) ;
}
}
# endif /* HAS_LIBCAP_NG */
2019-10-03 21:23:53 +00:00
2011-09-28 19:13:20 +00:00
/* main loop */
while ( ! quitting )
{
2018-03-13 10:43:07 +00:00
# ifdef USE_TIME_AS_BOOTID
2011-09-28 19:13:20 +00:00
/* Correct startup_time if it was set with a RTC close to 0 */
2018-03-13 10:43:07 +00:00
if ( ( upnp_bootid < 60 * 60 * 24 ) & & ( time ( NULL ) > 60 * 60 * 24 ) )
2011-09-28 19:13:20 +00:00
{
2018-03-13 10:43:07 +00:00
upnp_bootid = time ( NULL ) ;
2012-03-01 01:44:38 +00:00
}
2018-03-13 10:43:07 +00:00
# endif
2018-05-02 07:40:12 +00:00
# if !defined(TOMATO) && defined(ENABLE_LEASEFILE) && defined(LEASEFILE_USE_REMAINING_TIME)
if ( should_rewrite_leasefile )
{
lease_file_rewrite ( ) ;
should_rewrite_leasefile = 0 ;
}
# endif /* !TOMATO && ENABLE_LEASEFILE && LEASEFILE_USE_REMAINING_TIME */
2011-09-28 19:13:20 +00:00
/* send public address change notifications if needed */
if ( should_send_public_address_change_notif )
{
2014-03-13 10:31:51 +00:00
syslog ( LOG_INFO , " should send external iface address change notification(s) " ) ;
2018-07-06 11:23:22 +00:00
if ( GETFLAG ( PERFORMSTUNMASK ) )
2018-05-19 11:32:42 +00:00
update_ext_ip_addr_from_stun ( 0 ) ;
2018-05-19 11:32:04 +00:00
if ( ! use_ext_ip_addr )
{
char if_addr [ INET_ADDRSTRLEN ] ;
struct in_addr addr ;
if ( getifaddr ( ext_if_name , if_addr , INET_ADDRSTRLEN , & addr , NULL ) = = 0 ) {
int reserved = addr_is_reserved ( & addr ) ;
if ( disable_port_forwarding & & ! reserved ) {
syslog ( LOG_INFO , " Public IP address %s on ext interface %s: Port forwarding is enabled " , if_addr , ext_if_name ) ;
} else if ( ! disable_port_forwarding & & reserved ) {
syslog ( LOG_INFO , " Reserved / private IP address %s on ext interface %s: Port forwarding is impossible " , if_addr , ext_if_name ) ;
2018-05-19 11:32:42 +00:00
syslog ( LOG_INFO , " You are probably behind NAT, enable option ext_perform_stun=yes to detect public IP address " ) ;
2018-05-19 11:32:04 +00:00
}
disable_port_forwarding = reserved ;
}
}
2011-09-28 19:13:20 +00:00
# ifdef ENABLE_NATPMP
if ( GETFLAG ( ENABLENATPMPMASK ) )
SendNATPMPPublicAddressChangeNotification ( snatpmp , addr_count ) ;
# endif
# ifdef ENABLE_EVENTS
if ( GETFLAG ( ENABLEUPNPMASK ) )
{
upnp_event_var_change_notify ( EWanIPC ) ;
}
2017-11-02 16:27:24 +00:00
# endif
# ifdef ENABLE_PCP
if ( GETFLAG ( ENABLENATPMPMASK ) )
{
2017-11-27 22:04:22 +00:00
# ifdef ENABLE_IPV6
PCPPublicAddressChanged ( snatpmp , addr_count , spcp_v6 ) ;
# else /* IPv4 only */
PCPPublicAddressChanged ( snatpmp , addr_count ) ;
# endif
2017-11-02 16:27:24 +00:00
}
2011-09-28 19:13:20 +00:00
# endif
should_send_public_address_change_notif = 0 ;
}
/* Check if we need to send SSDP NOTIFY messages and do it if
* needed */
2018-04-12 08:07:11 +00:00
if ( upnp_gettimeofday ( & timeofday ) < 0 )
2011-09-28 19:13:20 +00:00
{
syslog ( LOG_ERR , " gettimeofday(): %m " ) ;
timeout . tv_sec = v . notify_interval ;
timeout . tv_usec = 0 ;
}
else
{
/* the comparaison is not very precise but who cares ? */
if ( timeofday . tv_sec > = ( lasttimeofday . tv_sec + v . notify_interval ) )
{
if ( GETFLAG ( ENABLEUPNPMASK ) )
SendSSDPNotifies2 ( snotify ,
( unsigned short ) v . port ,
2014-04-09 13:35:06 +00:00
# ifdef ENABLE_HTTPS
( unsigned short ) v . https_port ,
# endif
2011-09-28 19:13:20 +00:00
v . notify_interval < < 1 ) ;
memcpy ( & lasttimeofday , & timeofday , sizeof ( struct timeval ) ) ;
timeout . tv_sec = v . notify_interval ;
timeout . tv_usec = 0 ;
}
else
{
timeout . tv_sec = lasttimeofday . tv_sec + v . notify_interval
- timeofday . tv_sec ;
if ( timeofday . tv_usec > lasttimeofday . tv_usec )
{
timeout . tv_usec = 1000000 + lasttimeofday . tv_usec
- timeofday . tv_usec ;
timeout . tv_sec - - ;
}
else
{
timeout . tv_usec = lasttimeofday . tv_usec - timeofday . tv_usec ;
}
}
}
/* remove unused rules */
if ( v . clean_ruleset_interval
& & ( timeofday . tv_sec > = checktime . tv_sec + v . clean_ruleset_interval ) )
{
if ( rule_list )
{
remove_unused_rules ( rule_list ) ;
rule_list = NULL ;
}
else
{
rule_list = get_upnp_rules_state_list ( v . clean_ruleset_threshold ) ;
}
memcpy ( & checktime , & timeofday , sizeof ( struct timeval ) ) ;
}
/* Remove expired port mappings, based on UPnP IGD LeaseDuration
* or NAT - PMP lifetime ) */
if ( nextruletoclean_timestamp
2012-05-01 09:52:21 +00:00
& & ( ( unsigned int ) timeofday . tv_sec > = nextruletoclean_timestamp ) )
2011-09-28 19:13:20 +00:00
{
syslog ( LOG_DEBUG , " cleaning expired Port Mappings " ) ;
get_upnp_rules_state_list ( 0 ) ;
}
if ( nextruletoclean_timestamp
2012-05-01 09:52:21 +00:00
& & ( ( unsigned int ) timeout . tv_sec > = ( nextruletoclean_timestamp - timeofday . tv_sec ) ) )
2011-09-28 19:13:20 +00:00
{
timeout . tv_sec = nextruletoclean_timestamp - timeofday . tv_sec ;
timeout . tv_usec = 0 ;
syslog ( LOG_DEBUG , " setting timeout to %u sec " ,
( unsigned ) timeout . tv_sec ) ;
}
2014-05-10 11:09:48 +00:00
# ifdef ENABLE_UPNPPINHOLE
2012-04-22 00:58:27 +00:00
/* Clean up expired IPv6 PinHoles */
2012-05-08 20:51:23 +00:00
next_pinhole_ts = 0 ;
upnp_clean_expired_pinholes ( & next_pinhole_ts ) ;
if ( next_pinhole_ts & &
2012-05-09 21:28:26 +00:00
timeout . tv_sec > = ( int ) ( next_pinhole_ts - timeofday . tv_sec ) ) {
2012-05-08 20:51:23 +00:00
timeout . tv_sec = next_pinhole_ts - timeofday . tv_sec ;
timeout . tv_usec = 0 ;
}
2014-05-10 11:09:48 +00:00
# endif /* ENABLE_UPNPPINHOLE */
2011-09-28 19:13:20 +00:00
/* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */
FD_ZERO ( & readset ) ;
2012-02-07 00:26:15 +00:00
FD_ZERO ( & writeset ) ;
2011-09-28 19:13:20 +00:00
2012-03-01 01:44:38 +00:00
if ( sudp > = 0 )
2011-09-28 19:13:20 +00:00
{
FD_SET ( sudp , & readset ) ;
max_fd = MAX ( max_fd , sudp ) ;
# ifdef USE_IFACEWATCHER
if ( sifacewatcher > = 0 )
{
FD_SET ( sifacewatcher , & readset ) ;
max_fd = MAX ( max_fd , sifacewatcher ) ;
}
# endif
}
2012-03-01 01:44:38 +00:00
if ( shttpl > = 0 )
2011-09-28 19:13:20 +00:00
{
FD_SET ( shttpl , & readset ) ;
max_fd = MAX ( max_fd , shttpl ) ;
}
2014-03-13 10:30:24 +00:00
# if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6)
if ( shttpl_v4 > = 0 )
{
FD_SET ( shttpl_v4 , & readset ) ;
max_fd = MAX ( max_fd , shttpl_v4 ) ;
}
# endif
2014-04-09 13:35:06 +00:00
# ifdef ENABLE_HTTPS
if ( shttpsl > = 0 )
{
FD_SET ( shttpsl , & readset ) ;
max_fd = MAX ( max_fd , shttpsl ) ;
}
# if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6)
if ( shttpsl_v4 > = 0 )
{
FD_SET ( shttpsl_v4 , & readset ) ;
max_fd = MAX ( max_fd , shttpsl_v4 ) ;
}
# endif
# endif /* ENABLE_HTTPS */
2011-09-28 19:13:20 +00:00
# ifdef ENABLE_IPV6
if ( sudpv6 > = 0 )
{
FD_SET ( sudpv6 , & readset ) ;
max_fd = MAX ( max_fd , sudpv6 ) ;
}
# endif
# ifdef ENABLE_NFQUEUE
2012-03-01 01:44:38 +00:00
if ( nfqh > = 0 )
2011-09-28 19:13:20 +00:00
{
FD_SET ( nfqh , & readset ) ;
max_fd = MAX ( max_fd , nfqh ) ;
}
# endif
i = 0 ; /* active HTTP connections count */
for ( e = upnphttphead . lh_first ; e ! = NULL ; e = e - > entries . le_next )
{
2012-02-07 00:26:15 +00:00
if ( e - > socket > = 0 )
2011-09-28 19:13:20 +00:00
{
2012-02-07 00:26:15 +00:00
if ( e - > state < = EWaitingForHttpContent )
FD_SET ( e - > socket , & readset ) ;
else if ( e - > state = = ESendingAndClosing )
FD_SET ( e - > socket , & writeset ) ;
else
continue ;
max_fd = MAX ( max_fd , e - > socket ) ;
2011-09-28 19:13:20 +00:00
i + + ;
}
}
/* for debug */
# ifdef DEBUG
if ( i > 1 )
{
syslog ( LOG_DEBUG , " %d active incoming HTTP connections " , i ) ;
}
# endif
# ifdef ENABLE_NATPMP
for ( i = 0 ; i < addr_count ; i + + ) {
if ( snatpmp [ i ] > = 0 ) {
FD_SET ( snatpmp [ i ] , & readset ) ;
max_fd = MAX ( max_fd , snatpmp [ i ] ) ;
}
}
# endif
2014-03-24 11:07:31 +00:00
# if defined(ENABLE_IPV6) && defined(ENABLE_PCP)
if ( spcp_v6 > = 0 ) {
FD_SET ( spcp_v6 , & readset ) ;
max_fd = MAX ( max_fd , spcp_v6 ) ;
}
# endif
2011-09-28 19:13:20 +00:00
# ifdef USE_MINIUPNPDCTL
if ( sctl > = 0 ) {
FD_SET ( sctl , & readset ) ;
max_fd = MAX ( max_fd , sctl ) ;
}
2012-03-01 01:44:38 +00:00
2011-09-28 19:13:20 +00:00
for ( ectl = ctllisthead . lh_first ; ectl ; ectl = ectl - > entries . le_next )
{
if ( ectl - > socket > = 0 ) {
FD_SET ( ectl - > socket , & readset ) ;
max_fd = MAX ( max_fd , ectl - > socket ) ;
}
}
# endif
# ifdef ENABLE_EVENTS
upnpevents_selectfds ( & readset , & writeset , & max_fd ) ;
# endif
2014-02-25 10:10:05 +00:00
/* queued "sendto" */
{
struct timeval next_send ;
i = get_next_scheduled_send ( & next_send ) ;
if ( i > 0 ) {
# ifdef DEBUG
syslog ( LOG_DEBUG , " %d queued sendto " , i ) ;
# endif
i = get_sendto_fds ( & writeset , & max_fd , & timeofday ) ;
if ( timeofday . tv_sec > next_send . tv_sec | |
( timeofday . tv_sec = = next_send . tv_sec & & timeofday . tv_usec > = next_send . tv_usec ) ) {
if ( i > 0 ) {
timeout . tv_sec = 0 ;
timeout . tv_usec = 0 ;
}
} else {
struct timeval tmp_timeout ;
tmp_timeout . tv_sec = ( next_send . tv_sec - timeofday . tv_sec ) ;
tmp_timeout . tv_usec = ( next_send . tv_usec - timeofday . tv_usec ) ;
if ( tmp_timeout . tv_usec < 0 ) {
tmp_timeout . tv_usec + = 1000000 ;
tmp_timeout . tv_sec - - ;
}
if ( timeout . tv_sec > tmp_timeout . tv_sec
| | ( timeout . tv_sec = = tmp_timeout . tv_sec & & timeout . tv_usec > tmp_timeout . tv_usec ) ) {
timeout . tv_sec = tmp_timeout . tv_sec ;
timeout . tv_usec = tmp_timeout . tv_usec ;
}
}
}
}
2011-09-28 19:13:20 +00:00
if ( select ( max_fd + 1 , & readset , & writeset , 0 , & timeout ) < 0 )
{
if ( quitting ) goto shutdown ;
2015-06-06 03:19:52 +00:00
# ifdef TOMATO
if ( gotusr2 )
{
gotusr2 = 0 ;
tomato_helper ( ) ;
continue ;
}
2015-06-09 13:13:25 +00:00
# endif /* TOMATO */
2011-09-28 19:13:20 +00:00
if ( errno = = EINTR ) continue ; /* interrupted by a signal, start again */
syslog ( LOG_ERR , " select(all): %m " ) ;
syslog ( LOG_ERR , " Failed to select open sockets. EXITING " ) ;
return 1 ; /* very serious cause of error */
}
2014-04-11 07:31:02 +00:00
i = try_sendto ( & writeset ) ;
if ( i < 0 ) {
syslog ( LOG_ERR , " try_sendto failed to send %d packets " , - i ) ;
2014-02-25 10:10:05 +00:00
}
2011-09-28 19:13:20 +00:00
# ifdef USE_MINIUPNPDCTL
for ( ectl = ctllisthead . lh_first ; ectl ; )
{
ectlnext = ectl - > entries . le_next ;
if ( ( ectl - > socket > = 0 ) & & FD_ISSET ( ectl - > socket , & readset ) )
{
char buf [ 256 ] ;
int l ;
l = read ( ectl - > socket , buf , sizeof ( buf ) ) ;
if ( l > 0 )
{
/*write(ectl->socket, buf, l);*/
write_command_line ( ectl - > socket , argc , argv ) ;
2012-09-28 09:04:04 +00:00
# ifndef DISABLE_CONFIG_FILE
2011-09-28 19:13:20 +00:00
write_option_list ( ectl - > socket ) ;
2012-09-28 09:04:04 +00:00
# endif
2011-09-28 19:13:20 +00:00
write_permlist ( ectl - > socket , upnppermlist , num_upnpperm ) ;
write_upnphttp_details ( ectl - > socket , upnphttphead . lh_first ) ;
write_ctlsockets_list ( ectl - > socket , ctllisthead . lh_first ) ;
write_ruleset_details ( ectl - > socket ) ;
# ifdef ENABLE_EVENTS
write_events_details ( ectl - > socket ) ;
# endif
/* close the socket */
close ( ectl - > socket ) ;
ectl - > socket = - 1 ;
}
else
{
close ( ectl - > socket ) ;
ectl - > socket = - 1 ;
}
}
if ( ectl - > socket < 0 )
{
LIST_REMOVE ( ectl , entries ) ;
free ( ectl ) ;
}
ectl = ectlnext ;
}
if ( ( sctl > = 0 ) & & FD_ISSET ( sctl , & readset ) )
{
int s ;
struct sockaddr_un clientname ;
struct ctlelem * tmp ;
socklen_t clientnamelen = sizeof ( struct sockaddr_un ) ;
2012-10-16 16:55:47 +00:00
/*syslog(LOG_DEBUG, "sctl!");*/
2011-09-28 19:13:20 +00:00
s = accept ( sctl , ( struct sockaddr * ) & clientname ,
& clientnamelen ) ;
syslog ( LOG_DEBUG , " sctl! : '%s' " , clientname . sun_path ) ;
tmp = malloc ( sizeof ( struct ctlelem ) ) ;
2012-12-11 21:10:57 +00:00
if ( tmp = = NULL )
{
syslog ( LOG_ERR , " Unable to allocate memory for ctlelem in main() " ) ;
close ( s ) ;
}
else
{
tmp - > socket = s ;
LIST_INSERT_HEAD ( & ctllisthead , tmp , entries ) ;
}
2011-09-28 19:13:20 +00:00
}
# endif
# ifdef ENABLE_EVENTS
upnpevents_processfds ( & readset , & writeset ) ;
# endif
# ifdef ENABLE_NATPMP
/* process NAT-PMP packets */
for ( i = 0 ; i < addr_count ; i + + )
{
if ( ( snatpmp [ i ] > = 0 ) & & FD_ISSET ( snatpmp [ i ] , & readset ) )
{
2013-07-09 13:36:53 +00:00
unsigned char msg_buff [ PCP_MAX_LEN ] ;
struct sockaddr_in senderaddr ;
2014-03-24 11:07:31 +00:00
socklen_t senderaddrlen ;
2013-07-09 13:36:53 +00:00
int len ;
memset ( msg_buff , 0 , PCP_MAX_LEN ) ;
2014-03-24 11:07:31 +00:00
senderaddrlen = sizeof ( senderaddr ) ;
len = ReceiveNATPMPOrPCPPacket ( snatpmp [ i ] ,
( struct sockaddr * ) & senderaddr ,
& senderaddrlen ,
2014-05-19 13:27:34 +00:00
NULL ,
2014-03-24 11:07:31 +00:00
msg_buff , sizeof ( msg_buff ) ) ;
2013-07-09 13:36:53 +00:00
if ( len < 1 )
continue ;
2014-10-30 19:37:35 +00:00
# ifdef ENABLE_PCP
if ( msg_buff [ 0 ] = = 0 ) { /* version equals to 0 -> means NAT-PMP */
/* Check if the packet is coming from a LAN to enforce RFC6886 :
* The NAT gateway MUST NOT accept mapping requests destined to the NAT
* gateway ' s external IP address or received on its external network
* interface . Only packets received on the internal interface ( s ) with a
* destination address matching the internal address ( es ) of the NAT
* gateway should be allowed . */
/* TODO : move to ProcessIncomingNATPMPPacket() ? */
lan_addr = get_lan_for_peer ( ( struct sockaddr * ) & senderaddr ) ;
if ( lan_addr = = NULL ) {
char sender_str [ 64 ] ;
sockaddr_to_string ( ( struct sockaddr * ) & senderaddr , sender_str , sizeof ( sender_str ) ) ;
syslog ( LOG_WARNING , " NAT-PMP packet sender %s not from a LAN, ignoring " ,
sender_str ) ;
continue ;
}
ProcessIncomingNATPMPPacket ( snatpmp [ i ] , msg_buff , len ,
& senderaddr ) ;
} else { /* everything else can be PCP */
ProcessIncomingPCPPacket ( snatpmp [ i ] , msg_buff , len ,
( struct sockaddr * ) & senderaddr , NULL ) ;
}
# else
2014-10-22 08:54:07 +00:00
/* Check if the packet is coming from a LAN to enforce RFC6886 :
* The NAT gateway MUST NOT accept mapping requests destined to the NAT
* gateway ' s external IP address or received on its external network
* interface . Only packets received on the internal interface ( s ) with a
* destination address matching the internal address ( es ) of the NAT
* gateway should be allowed . */
2014-10-30 19:37:35 +00:00
/* TODO : move to ProcessIncomingNATPMPPacket() ? */
2014-10-22 08:54:07 +00:00
lan_addr = get_lan_for_peer ( ( struct sockaddr * ) & senderaddr ) ;
if ( lan_addr = = NULL ) {
char sender_str [ 64 ] ;
sockaddr_to_string ( ( struct sockaddr * ) & senderaddr , sender_str , sizeof ( sender_str ) ) ;
2014-10-30 19:37:35 +00:00
syslog ( LOG_WARNING , " NAT-PMP packet sender %s not from a LAN, ignoring " ,
2014-10-22 08:54:07 +00:00
sender_str ) ;
continue ;
}
2013-07-09 13:36:53 +00:00
ProcessIncomingNATPMPPacket ( snatpmp [ i ] , msg_buff , len , & senderaddr ) ;
# endif
2011-09-28 19:13:20 +00:00
}
}
2014-03-24 11:07:31 +00:00
# endif
# if defined(ENABLE_IPV6) && defined(ENABLE_PCP)
/* in IPv6, only PCP is supported, not NAT-PMP */
if ( spcp_v6 > = 0 & & FD_ISSET ( spcp_v6 , & readset ) )
{
unsigned char msg_buff [ PCP_MAX_LEN ] ;
struct sockaddr_in6 senderaddr ;
socklen_t senderaddrlen ;
2014-05-19 13:27:34 +00:00
struct sockaddr_in6 receiveraddr ;
2014-03-24 11:07:31 +00:00
int len ;
memset ( msg_buff , 0 , PCP_MAX_LEN ) ;
senderaddrlen = sizeof ( senderaddr ) ;
len = ReceiveNATPMPOrPCPPacket ( spcp_v6 ,
( struct sockaddr * ) & senderaddr ,
& senderaddrlen ,
2014-05-19 13:27:34 +00:00
& receiveraddr ,
2014-03-24 11:07:31 +00:00
msg_buff , sizeof ( msg_buff ) ) ;
if ( len > = 1 )
ProcessIncomingPCPPacket ( spcp_v6 , msg_buff , len ,
2014-05-19 13:27:34 +00:00
( struct sockaddr * ) & senderaddr ,
& receiveraddr ) ;
2014-03-24 11:07:31 +00:00
}
2011-09-28 19:13:20 +00:00
# endif
/* process SSDP packets */
if ( sudp > = 0 & & FD_ISSET ( sudp , & readset ) )
{
/*syslog(LOG_INFO, "Received UDP Packet");*/
2014-04-09 13:35:06 +00:00
# ifdef ENABLE_HTTPS
ProcessSSDPRequest ( sudp , ( unsigned short ) v . port , ( unsigned short ) v . https_port ) ;
# else
2011-09-28 19:13:20 +00:00
ProcessSSDPRequest ( sudp , ( unsigned short ) v . port ) ;
2014-04-09 13:35:06 +00:00
# endif
2011-09-28 19:13:20 +00:00
}
# ifdef ENABLE_IPV6
if ( sudpv6 > = 0 & & FD_ISSET ( sudpv6 , & readset ) )
{
syslog ( LOG_INFO , " Received UDP Packet (IPv6) " ) ;
2014-04-09 13:35:06 +00:00
# ifdef ENABLE_HTTPS
ProcessSSDPRequest ( sudpv6 , ( unsigned short ) v . port , ( unsigned short ) v . https_port ) ;
# else
2011-09-28 19:13:20 +00:00
ProcessSSDPRequest ( sudpv6 , ( unsigned short ) v . port ) ;
2014-04-09 13:35:06 +00:00
# endif
2011-09-28 19:13:20 +00:00
}
# endif
# ifdef USE_IFACEWATCHER
/* process kernel notifications */
if ( sifacewatcher > = 0 & & FD_ISSET ( sifacewatcher , & readset ) )
ProcessInterfaceWatchNotify ( sifacewatcher ) ;
# endif
/* process active HTTP connections */
/* LIST_FOREACH macro is not available under linux */
for ( e = upnphttphead . lh_first ; e ! = NULL ; e = e - > entries . le_next )
{
2012-02-07 00:26:15 +00:00
if ( e - > socket > = 0 )
2011-09-28 19:13:20 +00:00
{
2012-02-07 00:26:15 +00:00
if ( FD_ISSET ( e - > socket , & readset ) | |
FD_ISSET ( e - > socket , & writeset ) )
{
Process_upnphttp ( e ) ;
}
2011-09-28 19:13:20 +00:00
}
}
/* process incoming HTTP connections */
if ( shttpl > = 0 & & FD_ISSET ( shttpl , & readset ) )
{
2014-03-13 10:30:24 +00:00
struct upnphttp * tmp ;
2014-04-20 16:40:26 +00:00
tmp = ProcessIncomingHTTP ( shttpl , " HTTP " ) ;
2014-03-13 10:30:24 +00:00
if ( tmp )
2011-09-28 19:13:20 +00:00
{
2014-03-13 10:30:24 +00:00
LIST_INSERT_HEAD ( & upnphttphead , tmp , entries ) ;
2011-09-28 19:13:20 +00:00
}
2014-03-13 10:30:24 +00:00
}
# if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6)
if ( shttpl_v4 > = 0 & & FD_ISSET ( shttpl_v4 , & readset ) )
{
struct upnphttp * tmp ;
2014-04-20 16:40:26 +00:00
tmp = ProcessIncomingHTTP ( shttpl_v4 , " HTTP " ) ;
2014-03-13 10:30:24 +00:00
if ( tmp )
2011-09-28 19:13:20 +00:00
{
2014-03-13 10:30:24 +00:00
LIST_INSERT_HEAD ( & upnphttphead , tmp , entries ) ;
2011-09-28 19:13:20 +00:00
}
}
2014-03-13 10:30:24 +00:00
# endif
2014-04-09 13:35:06 +00:00
# ifdef ENABLE_HTTPS
if ( shttpsl > = 0 & & FD_ISSET ( shttpsl , & readset ) )
{
struct upnphttp * tmp ;
2014-04-20 16:40:26 +00:00
tmp = ProcessIncomingHTTP ( shttpsl , " HTTPS " ) ;
2014-04-09 13:35:06 +00:00
if ( tmp )
{
InitSSL_upnphttp ( tmp ) ;
LIST_INSERT_HEAD ( & upnphttphead , tmp , entries ) ;
}
}
# if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6)
if ( shttpsl_v4 > = 0 & & FD_ISSET ( shttpsl_v4 , & readset ) )
{
struct upnphttp * tmp ;
2014-04-20 16:40:26 +00:00
tmp = ProcessIncomingHTTP ( shttpsl_v4 , " HTTPS " ) ;
2014-04-09 13:35:06 +00:00
if ( tmp )
{
InitSSL_upnphttp ( tmp ) ;
LIST_INSERT_HEAD ( & upnphttphead , tmp , entries ) ;
}
}
# endif
# endif /* ENABLE_HTTPS */
2011-09-28 19:13:20 +00:00
# ifdef ENABLE_NFQUEUE
/* process NFQ packets */
if ( nfqh > = 0 & & FD_ISSET ( nfqh , & readset ) )
{
/* syslog(LOG_INFO, "Received NFQUEUE Packet");*/
ProcessNFQUEUE ( nfqh ) ;
}
# endif
/* delete finished HTTP connections */
for ( e = upnphttphead . lh_first ; e ! = NULL ; )
{
next = e - > entries . le_next ;
2012-02-07 00:26:15 +00:00
if ( e - > state > = EToDelete )
2011-09-28 19:13:20 +00:00
{
LIST_REMOVE ( e , entries ) ;
Delete_upnphttp ( e ) ;
}
e = next ;
}
} /* end of main loop */
shutdown :
2014-02-28 12:16:22 +00:00
syslog ( LOG_NOTICE , " shutting down MiniUPnPd " ) ;
2014-02-25 10:36:58 +00:00
/* send good-bye */
if ( GETFLAG ( ENABLEUPNPMASK ) )
{
# ifndef ENABLE_IPV6
if ( SendSSDPGoodbye ( snotify , addr_count ) < 0 )
# else
if ( SendSSDPGoodbye ( snotify , addr_count * 2 ) < 0 )
# endif
{
syslog ( LOG_ERR , " Failed to broadcast good-bye notifications " ) ;
}
}
/* try to send pending packets */
finalize_sendto ( ) ;
2015-06-06 03:19:52 +00:00
# ifdef TOMATO
tomato_save ( " /etc/upnp/data " ) ;
2015-06-09 13:13:25 +00:00
# endif /* TOMATO */
2018-05-02 07:40:12 +00:00
# if defined(ENABLE_LEASEFILE) && defined(LEASEFILE_USE_REMAINING_TIME)
lease_file_rewrite ( ) ;
# endif /* ENABLE_LEASEFILE && LEASEFILE_USE_REMAINING_TIME */
2011-09-28 19:13:20 +00:00
/* close out open sockets */
while ( upnphttphead . lh_first ! = NULL )
{
e = upnphttphead . lh_first ;
LIST_REMOVE ( e , entries ) ;
Delete_upnphttp ( e ) ;
}
if ( sudp > = 0 ) close ( sudp ) ;
if ( shttpl > = 0 ) close ( shttpl ) ;
2014-03-13 10:30:24 +00:00
# if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6)
if ( shttpl_v4 > = 0 ) close ( shttpl_v4 ) ;
# endif
2011-09-28 19:13:20 +00:00
# ifdef ENABLE_IPV6
if ( sudpv6 > = 0 ) close ( sudpv6 ) ;
# endif
# ifdef USE_IFACEWATCHER
if ( sifacewatcher > = 0 ) close ( sifacewatcher ) ;
# endif
# ifdef ENABLE_NATPMP
for ( i = 0 ; i < addr_count ; i + + ) {
if ( snatpmp [ i ] > = 0 )
{
close ( snatpmp [ i ] ) ;
snatpmp [ i ] = - 1 ;
}
}
# endif
2014-03-24 11:07:31 +00:00
# if defined(ENABLE_IPV6) && defined(ENABLE_PCP)
if ( spcp_v6 > = 0 )
{
close ( spcp_v6 ) ;
spcp_v6 = - 1 ;
}
# endif
2011-09-28 19:13:20 +00:00
# ifdef USE_MINIUPNPDCTL
if ( sctl > = 0 )
{
close ( sctl ) ;
sctl = - 1 ;
if ( unlink ( " /var/run/miniupnpd.ctl " ) < 0 )
{
syslog ( LOG_ERR , " unlink() %m " ) ;
}
}
# endif
2012-03-01 01:44:38 +00:00
2011-09-28 19:13:20 +00:00
if ( GETFLAG ( ENABLEUPNPMASK ) )
{
2012-04-06 17:30:19 +00:00
# ifndef ENABLE_IPV6
for ( i = 0 ; i < addr_count ; i + + )
# else
for ( i = 0 ; i < addr_count * 2 ; i + + )
# endif
2011-09-28 19:13:20 +00:00
close ( snotify [ i ] ) ;
}
2014-02-25 10:36:58 +00:00
/* remove pidfile */
2020-06-03 21:43:58 +00:00
# ifndef NO_BACKGROUND_NO_PIDFILE
2012-03-31 07:22:50 +00:00
if ( pidfilename & & ( unlink ( pidfilename ) < 0 ) )
2011-09-28 19:13:20 +00:00
{
syslog ( LOG_ERR , " Failed to remove pidfile %s: %m " , pidfilename ) ;
}
2020-06-03 21:43:58 +00:00
# endif
2011-09-28 19:13:20 +00:00
/* delete lists */
while ( lan_addrs . lh_first ! = NULL )
{
lan_addr = lan_addrs . lh_first ;
LIST_REMOVE ( lan_addrs . lh_first , list ) ;
free ( lan_addr ) ;
}
2014-04-09 14:09:31 +00:00
# ifdef ENABLE_HTTPS
free_ssl ( ) ;
# endif
2011-09-28 19:13:20 +00:00
# ifdef ENABLE_NATPMP
free ( snatpmp ) ;
# endif
free ( snotify ) ;
2012-03-01 01:44:38 +00:00
closelog ( ) ;
2012-06-29 19:39:34 +00:00
# ifndef DISABLE_CONFIG_FILE
2011-09-28 19:13:20 +00:00
freeoptions ( ) ;
2012-06-29 19:39:34 +00:00
# endif
2012-03-01 01:44:38 +00:00
2019-09-30 07:12:08 +00:00
shutdown_redirect ( ) ;
2011-09-28 19:13:20 +00:00
return 0 ;
}