1 /* $Id: miniupnpd.c,v 1.113 2009/09/04 16:14:04 nanard Exp $ */
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2006-2008 Thomas Bernard
5 * This software is subject to the conditions detailed
6 * in the LICENCE file provided within the distribution */
13 #include <sys/types.h>
14 #include <sys/socket.h>
15 #include <netinet/in.h>
16 #include <arpa/inet.h>
23 #include <sys/param.h>
27 /* for BSD's sysctl */
28 #include <sys/sysctl.h>
33 #ifdef USE_MINIUPNPDCTL
37 #include "upnpglobalvars.h"
39 #include "upnpdescgen.h"
40 #include "miniupnpdpath.h"
41 #include "getifaddr.h"
45 #include "upnpredirect.h"
46 #include "miniupnpdtypes.h"
47 #include "daemonize.h"
48 #include "upnpevents.h"
52 #include "commonrdr.h"
54 #ifndef DEFAULT_CONFIG
55 #define DEFAULT_CONFIG "/etc/miniupnpd.conf"
58 #ifdef USE_MINIUPNPDCTL
61 LIST_ENTRY(ctlelem
) entries
;
65 /* MAX_LAN_ADDR : maximum number of interfaces
66 * to listen to SSDP traffic */
67 /*#define MAX_LAN_ADDR (4)*/
69 static volatile int quitting
= 0;
73 static volatile int gotusr2
= 0;
75 static void sigusr2(int sig
)
80 static void tomato_save(const char *fname
)
92 strcpy(tmp
, "/etc/upnp/saveXXXXXX");
93 if ((t
= mkstemp(tmp
)) != -1) {
94 if ((f
= fdopen(t
, "w")) != NULL
) {
96 while (upnp_get_redirection_infos_by_index(n
, &eport
, proto
, &iport
, iaddr
, sizeof(iaddr
), desc
, sizeof(desc
)) == 0) {
97 fprintf(f
, "%s %u %s %u [%s]\n", proto
, eport
, iaddr
, iport
, desc
);
110 static void tomato_load(void)
114 unsigned short eport
;
115 unsigned short iport
;
121 if ((f
= fopen("/etc/upnp/data", "r")) != NULL
) {
122 s
[sizeof(s
) - 1] = 0;
123 while (fgets(s
, sizeof(s
) - 1, f
)) {
124 if (sscanf(s
, "%3s %hu %31s %hu ", proto
, &eport
, iaddr
, &iport
) == 4) {
125 if (((a
= strchr(s
, '[')) != NULL
) && ((b
= strrchr(a
, ']')) != NULL
) && ((b
- a
) <= sizeof(desc
))) {
127 upnp_redirect(eport
, iaddr
, iport
, proto
, a
+ 1);
133 unlink("/etc/upnp/load");
136 static void tomato_delete(void)
140 unsigned short eport
;
141 unsigned short iport
;
147 if ((f
= fopen("/etc/upnp/delete", "r")) != NULL
) {
148 s
[sizeof(s
) - 1] = 0;
149 while (fgets(s
, sizeof(s
) - 1, f
)) {
150 if (sscanf(s
, "%3s %hu", proto
, &eport
) == 2) {
151 if (proto
[0] == '*') {
152 n
= upnp_get_portmapping_number_of_entries();
154 if (upnp_get_redirection_infos_by_index(n
, &eport
, proto
, &iport
, iaddr
, sizeof(iaddr
), desc
, sizeof(desc
)) == 0) {
155 upnp_delete_redirection(eport
, proto
);
161 upnp_delete_redirection(eport
, proto
);
166 unlink("/etc/upnp/delete");
170 static void tomato_helper(void)
174 if (stat("/etc/upnp/delete", &st
) == 0) {
178 if (stat("/etc/upnp/load", &st
) == 0) {
182 if (stat("/etc/upnp/save", &st
) == 0) {
183 tomato_save("/etc/upnp/data");
184 unlink("/etc/upnp/save");
187 if (stat("/etc/upnp/info", &st
) == 0) {
188 tomato_save("/etc/upnp/data.info");
189 unlink("/etc/upnp/info");
192 #endif /* 1 (tomato) */
195 /* OpenAndConfHTTPSocket() :
196 * setup the socket used to handle incoming HTTP connections. */
198 OpenAndConfHTTPSocket(unsigned short port
)
202 struct sockaddr_in listenname
;
204 if( (s
= socket(PF_INET
, SOCK_STREAM
, 0)) < 0)
206 syslog(LOG_ERR
, "socket(http): %m");
210 if(setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &i
, sizeof(i
)) < 0)
212 syslog(LOG_WARNING
, "setsockopt(http, SO_REUSEADDR): %m");
215 memset(&listenname
, 0, sizeof(struct sockaddr_in
));
216 listenname
.sin_family
= AF_INET
;
217 listenname
.sin_port
= htons(port
);
218 listenname
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
220 if(bind(s
, (struct sockaddr
*)&listenname
, sizeof(struct sockaddr_in
)) < 0)
222 syslog(LOG_ERR
, "bind(http): %m");
229 syslog(LOG_ERR
, "listen(http): %m");
237 /* Functions used to communicate with miniupnpdctl */
238 #ifdef USE_MINIUPNPDCTL
240 OpenAndConfCtlUnixSocket(const char * path
)
242 struct sockaddr_un localun
;
244 s
= socket(AF_UNIX
, SOCK_STREAM
, 0);
245 localun
.sun_family
= AF_UNIX
;
246 strncpy(localun
.sun_path
, path
,
247 sizeof(localun
.sun_path
));
248 if(bind(s
, (struct sockaddr
*)&localun
,
249 sizeof(struct sockaddr_un
)) < 0)
251 syslog(LOG_ERR
, "bind(sctl): %m");
255 else if(listen(s
, 5) < 0)
257 syslog(LOG_ERR
, "listen(sctl): %m");
265 write_upnphttp_details(int fd
, struct upnphttp
* e
)
271 len
= snprintf(buffer
, sizeof(buffer
),
272 "%d %d %s req_buf=%p(%dbytes) res_buf=%p(%dbytes alloc)\n",
273 e
->socket
, e
->state
, e
->HttpVer
,
274 e
->req_buf
, e
->req_buflen
,
275 e
->res_buf
, e
->res_buf_alloclen
);
276 write(fd
, buffer
, len
);
277 e
= e
->entries
.le_next
;
282 write_ctlsockets_list(int fd
, struct ctlelem
* e
)
288 len
= snprintf(buffer
, sizeof(buffer
),
289 "struct ctlelem: socket=%d\n", e
->socket
);
290 write(fd
, buffer
, len
);
291 e
= e
->entries
.le_next
;
296 write_option_list(int fd
)
301 for(i
=0; i
<num_options
; i
++)
303 len
= snprintf(buffer
, sizeof(buffer
),
305 ary_options
[i
].id
, ary_options
[i
].value
);
306 write(fd
, buffer
, len
);
312 /* Handler for the SIGTERM signal (kill)
313 * SIGINT is also handled */
317 /*int save_errno = errno;*/
318 signal(sig
, SIG_IGN
); /* Ignore this signal while we are quitting */
320 syslog(LOG_NOTICE
, "received signal %d, good-bye", sig
);
323 /*errno = save_errno;*/
326 /* record the startup time, for returning uptime */
328 set_startup_time(int sysuptime
)
330 startup_time
= time(NULL
);
333 /* use system uptime instead of daemon uptime */
334 #if defined(__linux__)
337 fd
= open("/proc/uptime", O_RDONLY
);
340 syslog(LOG_ERR
, "open(\"/proc/uptime\" : %m");
344 memset(buff
, 0, sizeof(buff
));
345 read(fd
, buff
, sizeof(buff
) - 1);
347 syslog(LOG_INFO
, "system uptime is %d seconds", uptime
);
349 startup_time
-= uptime
;
351 #elif defined(SOLARIS_KSTATS)
357 ksp
= kstat_lookup(kc
, "unix", 0, "system_misc");
358 if(ksp
&& (kstat_read(kc
, ksp
, NULL
) != -1))
360 void *ptr
= kstat_data_lookup(ksp
, "boot_time");
362 memcpy(&startup_time
, ptr
, sizeof(startup_time
));
364 syslog(LOG_ERR
, "cannot find boot_time kstat");
367 syslog(LOG_ERR
, "cannot open kstats for unix/0/system_misc: %m");
371 struct timeval boottime
;
372 size_t size
= sizeof(boottime
);
373 int name
[2] = { CTL_KERN
, KERN_BOOTTIME
};
374 if(sysctl(name
, 2, &boottime
, &size
, NULL
, 0) < 0)
376 syslog(LOG_ERR
, "sysctl(\"kern.boottime\") failed");
380 startup_time
= boottime
.tv_sec
;
386 /* structure containing variables used during "main loop"
387 * that are filled during the init */
388 struct runtime_vars
{
389 /* LAN IP addresses for SSDP traffic and HTTP */
390 /* moved to global vars */
392 /*struct lan_addr_s lan_addr[MAX_LAN_ADDR];*/
393 int port
; /* HTTP Port */
394 int notify_interval
; /* seconds between SSDP announces */
395 /* unused rules cleaning related variables : */
396 int clean_ruleset_threshold
; /* threshold for removing unused rules */
397 int clean_ruleset_interval
; /* (minimum) interval between checks */
401 * parse address with mask
407 parselanaddr(struct lan_addr_s
* lan_addr
, const char * str
)
413 while(*p
&& *p
!= '/' && !isspace(*p
))
419 while(*p
&& !isspace(*p
))
424 fprintf(stderr
, "Error parsing address/mask : %s\n", str
);
427 memcpy(lan_addr
->str
, str
, n
);
428 lan_addr
->str
[n
] = '\0';
429 if(!inet_aton(lan_addr
->str
, &lan_addr
->addr
))
431 fprintf(stderr
, "Error parsing address/mask : %s\n", str
);
434 lan_addr
->mask
.s_addr
= htonl(nbits
? (0xffffffff << (32 - nbits
)) : 0);
435 #ifdef MULTIPLE_EXTERNAL_IP
436 while(*p
&& isspace(*p
))
440 while(p
[n
] && !isspace(*p
))
443 memcpy(lan_addr
->ext_ip_str
, p
, n
);
444 lan_addr
->ext_ip_str
[n
] = '\0';
445 if(!inet_aton(lan_addr
->ext_ip_str
, &lan_addr
->ext_ip_addr
)) {
447 fprintf(stderr
, "Error parsing address : %s\n", lan_addr
->ext_ip_str
);
456 * 1) read configuration file
457 * 2) read command line arguments
460 * 5) check and write pid file
461 * 6) set startup time stamp
462 * 7) compute presentation URL
463 * 8) set signal handlers */
465 init(int argc
, char * * argv
, struct runtime_vars
* v
)
470 int options_flag
= 0;
473 /*const char * logfilename = 0;*/
474 const char * presurl
= 0;
475 const char * optionsfile
= DEFAULT_CONFIG
;
477 /* first check if "-f" option is used */
478 for(i
=2; i
<argc
; i
++)
480 if(0 == strcmp(argv
[i
-1], "-f"))
482 optionsfile
= argv
[i
];
488 /* set initial values */
489 SETFLAG(ENABLEUPNPMASK
);
491 /*v->n_lan_addr = 0;*/
493 v
->notify_interval
= 30; /* seconds between SSDP announces */
494 v
->clean_ruleset_threshold
= 20;
495 v
->clean_ruleset_interval
= 0; /* interval between ruleset check. 0=disabled */
497 /* read options file first since
498 * command line arguments have final say */
499 if(readoptionsfile(optionsfile
) < 0)
501 /* only error if file exists or using -f */
502 if(access(optionsfile
, F_OK
) == 0 || options_flag
)
503 fprintf(stderr
, "Error reading configuration file %s\n", optionsfile
);
507 for(i
=0; i
<num_options
; i
++)
509 switch(ary_options
[i
].id
)
512 ext_if_name
= ary_options
[i
].value
;
515 use_ext_ip_addr
= ary_options
[i
].value
;
517 case UPNPLISTENING_IP
:
518 if(n_lan_addr
< MAX_LAN_ADDR
)/* if(v->n_lan_addr < MAX_LAN_ADDR)*/
520 /*if(parselanaddr(&v->lan_addr[v->n_lan_addr],*/
521 if(parselanaddr(&lan_addr
[n_lan_addr
],
522 ary_options
[i
].value
) == 0)
523 n_lan_addr
++; /*v->n_lan_addr++; */
527 fprintf(stderr
, "Too many listening ips (max: %d), ignoring %s\n",
528 MAX_LAN_ADDR
, ary_options
[i
].value
);
532 v
->port
= atoi(ary_options
[i
].value
);
535 upstream_bitrate
= strtoul(ary_options
[i
].value
, 0, 0);
537 case UPNPBITRATE_DOWN
:
538 downstream_bitrate
= strtoul(ary_options
[i
].value
, 0, 0);
540 case UPNPPRESENTATIONURL
:
541 presurl
= ary_options
[i
].value
;
544 case UPNPFORWARDCHAIN
:
545 miniupnpd_forward_chain
= ary_options
[i
].value
;
548 miniupnpd_nat_chain
= ary_options
[i
].value
;
551 case UPNPNOTIFY_INTERVAL
:
552 v
->notify_interval
= atoi(ary_options
[i
].value
);
554 case UPNPSYSTEM_UPTIME
:
555 if(strcmp(ary_options
[i
].value
, "yes") == 0)
556 SETFLAG(SYSUPTIMEMASK
); /*sysuptime = 1;*/
559 if(strcmp(ary_options
[i
].value
, "yes") == 0)
560 SETFLAG(LOGPACKETSMASK
); /*logpackets = 1;*/
563 strncpy(uuidvalue
+5, ary_options
[i
].value
,
564 strlen(uuidvalue
+5) + 1);
567 strncpy(serialnumber
, ary_options
[i
].value
, SERIALNUMBER_MAX_LEN
);
568 serialnumber
[SERIALNUMBER_MAX_LEN
-1] = '\0';
570 case UPNPMODEL_NUMBER
:
571 strncpy(modelnumber
, ary_options
[i
].value
, MODELNUMBER_MAX_LEN
);
572 modelnumber
[MODELNUMBER_MAX_LEN
-1] = '\0';
574 case UPNPCLEANTHRESHOLD
:
575 v
->clean_ruleset_threshold
= atoi(ary_options
[i
].value
);
577 case UPNPCLEANINTERVAL
:
578 v
->clean_ruleset_interval
= atoi(ary_options
[i
].value
);
582 queue
= ary_options
[i
].value
;
585 tag
= ary_options
[i
].value
;
589 case UPNPENABLENATPMP
:
590 if(strcmp(ary_options
[i
].value
, "yes") == 0)
591 SETFLAG(ENABLENATPMPMASK
); /*enablenatpmp = 1;*/
593 if(atoi(ary_options
[i
].value
))
594 SETFLAG(ENABLENATPMPMASK
);
595 /*enablenatpmp = atoi(ary_options[i].value);*/
598 #ifdef PF_ENABLE_FILTER_RULES
600 if(strcmp(ary_options
[i
].value
, "no") == 0)
601 SETFLAG(PFNOQUICKRULESMASK
);
605 if(strcmp(ary_options
[i
].value
, "yes") != 0)
606 CLEARFLAG(ENABLEUPNPMASK
);
609 if(strcmp(ary_options
[i
].value
, "yes") == 0)
610 SETFLAG(SECUREMODEMASK
);
612 #ifdef ENABLE_LEASEFILE
614 lease_file
= ary_options
[i
].value
;
617 case UPNPMINISSDPDSOCKET
:
618 minissdpdsocketpath
= ary_options
[i
].value
;
621 fprintf(stderr
, "Unknown option in file %s\n",
627 /* command line arguments processing */
628 for(i
=1; i
<argc
; i
++)
632 fprintf(stderr
, "Unknown option: %s\n", argv
[i
]);
634 else switch(argv
[i
][1])
638 use_ext_ip_addr
= argv
[++i
];
640 fprintf(stderr
, "Option -%c takes one argument.\n", argv
[i
][1]);
644 v
->notify_interval
= atoi(argv
[++i
]);
646 fprintf(stderr
, "Option -%c takes one argument.\n", argv
[i
][1]);
650 strncpy(uuidvalue
+5, argv
[++i
], strlen(uuidvalue
+5) + 1);
652 fprintf(stderr
, "Option -%c takes one argument.\n", argv
[i
][1]);
656 strncpy(serialnumber
, argv
[++i
], SERIALNUMBER_MAX_LEN
);
658 fprintf(stderr
, "Option -%c takes one argument.\n", argv
[i
][1]);
659 serialnumber
[SERIALNUMBER_MAX_LEN
-1] = '\0';
663 strncpy(modelnumber
, argv
[++i
], MODELNUMBER_MAX_LEN
);
665 fprintf(stderr
, "Option -%c takes one argument.\n", argv
[i
][1]);
666 modelnumber
[MODELNUMBER_MAX_LEN
-1] = '\0';
670 /*enablenatpmp = 1;*/
671 SETFLAG(ENABLENATPMPMASK
);
676 SETFLAG(SYSUPTIMEMASK
);
679 logfilename = argv[++i];
683 SETFLAG(LOGPACKETSMASK
);
686 SETFLAG(SECUREMODEMASK
);
690 ext_if_name
= argv
[++i
];
692 fprintf(stderr
, "Option -%c takes one argument.\n", argv
[i
][1]);
699 fprintf(stderr
, "Option -%c takes one argument.\n", argv
[i
][1]);
705 fprintf(stderr
, "Option -%c takes one argument.\n", argv
[i
][1]);
710 v
->port
= atoi(argv
[++i
]);
712 fprintf(stderr
, "Option -%c takes one argument.\n", argv
[i
][1]);
716 pidfilename
= argv
[++i
];
718 fprintf(stderr
, "Option -%c takes one argument.\n", argv
[i
][1]);
727 fprintf(stderr
, "Option -%c takes one argument.\n", argv
[i
][1]);
732 downstream_bitrate
= strtoul(argv
[++i
], 0, 0);
733 upstream_bitrate
= strtoul(argv
[++i
], 0, 0);
736 fprintf(stderr
, "Option -%c takes two arguments.\n", argv
[i
][1]);
741 int address_already_there
= 0;
744 for(j
=0; j
<n_lan_addr
; j
++)/* for(j=0; j<v->n_lan_addr; j++)*/
746 struct lan_addr_s tmpaddr
;
747 parselanaddr(&tmpaddr
, argv
[i
]);
748 /*if(0 == strcmp(v->lan_addr[j].str, tmpaddr.str))*/
749 if(0 == strcmp(lan_addr
[j
].str
, tmpaddr
.str
))
750 address_already_there
= 1;
752 if(address_already_there
)
754 if(n_lan_addr
< MAX_LAN_ADDR
) /*if(v->n_lan_addr < MAX_LAN_ADDR)*/
756 /*v->lan_addr[v->n_lan_addr++] = argv[i];*/
757 /*if(parselanaddr(&v->lan_addr[v->n_lan_addr], argv[i]) == 0)*/
758 if(parselanaddr(&lan_addr
[n_lan_addr
], argv
[i
]) == 0)
759 n_lan_addr
++; /*v->n_lan_addr++;*/
763 fprintf(stderr
, "Too many listening ips (max: %d), ignoring %s\n",
764 MAX_LAN_ADDR
, argv
[i
]);
768 fprintf(stderr
, "Option -%c takes one argument.\n", argv
[i
][1]);
771 i
++; /* discarding, the config file is already read */
774 fprintf(stderr
, "Unknown option: %s\n", argv
[i
]);
777 if(!ext_if_name
|| (/*v->*/n_lan_addr
==0))
779 fprintf(stderr
, "Usage:\n\t"
780 "%s [-f config_file] [-i ext_ifname] [-o ext_ip]\n"
781 #ifndef ENABLE_NATPMP
782 "\t\t[-a listening_ip] [-p port] [-d] [-L] [-U] [-S]\n"
784 "\t\t[-a listening_ip] [-p port] [-d] [-L] [-U] [-S] [-N]\n"
786 /*"[-l logfile] " not functionnal */
787 "\t\t[-u uuid] [-s serial] [-m model_number] \n"
788 "\t\t[-t notify_interval] [-P pid_filename]\n"
790 "\t\t[-B down up] [-w url] [-q queue] [-T tag]\n"
792 "\t\t[-B down up] [-w url]\n"
794 "\nNotes:\n\tThere can be one or several listening_ips.\n"
795 "\tNotify interval is in seconds. Default is 30 seconds.\n"
796 "\tDefault pid file is %s.\n"
797 "\tWith -d miniupnpd will run as a standard program.\n"
798 "\t-L sets packet log in pf and ipf on.\n"
799 "\t-S sets \"secure\" mode : clients can only add mappings to their own ip\n"
800 "\t-U causes miniupnpd to report system uptime instead "
801 "of daemon uptime.\n"
802 "\t-B sets bitrates reported by daemon in bits per second.\n"
803 "\t-w sets the presentation url. Default is http address on port 80\n"
805 "\t-q sets the ALTQ queue in pf.\n"
806 "\t-T sets the tag name in pf.\n"
808 "", argv
[0], pidfilename
);
828 openlog_option
= LOG_PID
|LOG_CONS
;
831 openlog_option
|= LOG_PERROR
; /* also log on stderr */
834 openlog("miniupnpd", openlog_option
, LOG_MINIUPNPD
);
838 /* speed things up and ignore LOG_INFO and LOG_DEBUG */
839 setlogmask(LOG_UPTO(LOG_NOTICE
));
842 if(checkforrunning(pidfilename
) < 0)
844 syslog(LOG_ERR
, "MiniUPnPd is already running. EXITING");
848 set_startup_time(GETFLAG(SYSUPTIMEMASK
)/*sysuptime*/);
850 /* presentation url */
853 strncpy(presentationurl
, presurl
, PRESENTATIONURL_MAX_LEN
);
854 presentationurl
[PRESENTATIONURL_MAX_LEN
-1] = '\0';
858 snprintf(presentationurl
, PRESENTATIONURL_MAX_LEN
,
859 "http://%s/", lan_addr
[0].str
);
860 /*"http://%s:%d/", lan_addr[0].str, 80);*/
861 /*"http://%s:%d/", v->lan_addr[0].str, 80);*/
864 /* set signal handler */
865 memset(&sa
, 0, sizeof(struct sigaction
));
866 sa
.sa_handler
= sigterm
;
868 if (sigaction(SIGTERM
, &sa
, NULL
))
870 syslog(LOG_ERR
, "Failed to set %s handler. EXITING", "SIGTERM");
873 if (sigaction(SIGINT
, &sa
, NULL
))
875 syslog(LOG_ERR
, "Failed to set %s handler. EXITING", "SIGINT");
880 sa
.sa_handler
= sigusr2
;
881 sigaction(SIGUSR2
, &sa
, NULL
);
883 if(signal(SIGPIPE
, SIG_IGN
) == SIG_ERR
) {
884 syslog(LOG_ERR
, "Failed to ignore SIGPIPE signals");
887 if(init_redirect() < 0)
889 syslog(LOG_ERR
, "Failed to init redirection engine. EXITING");
893 writepidfile(pidfilename
, pid
);
895 #ifdef ENABLE_LEASEFILE
896 /*remove(lease_file);*/
897 syslog(LOG_INFO
, "Reloading rules from lease file");
898 reload_from_lease_file();
905 /* process HTTP or SSDP requests */
907 main(int argc
, char * * argv
)
910 int sudp
= -1, shttpl
= -1;
914 int snotify
[MAX_LAN_ADDR
];
915 LIST_HEAD(httplisthead
, upnphttp
) upnphttphead
;
916 struct upnphttp
* e
= 0;
917 struct upnphttp
* next
;
918 fd_set readset
; /* for select() */
922 struct timeval timeout
, timeofday
, lasttimeofday
= {0, 0};
924 #ifdef USE_MINIUPNPDCTL
926 LIST_HEAD(ctlstructhead
, ctlelem
) ctllisthead
;
927 struct ctlelem
* ectl
;
928 struct ctlelem
* ectlnext
;
930 struct runtime_vars v
;
931 /* variables used for the unused-rule cleanup process */
932 struct rule_state
* rule_list
= 0;
933 struct timeval checktime
= {0, 0};
935 if(init(argc
, argv
, &v
) != 0)
938 LIST_INIT(&upnphttphead
);
939 #ifdef USE_MINIUPNPDCTL
940 LIST_INIT(&ctllisthead
);
945 !GETFLAG(ENABLENATPMPMASK
) &&
947 !GETFLAG(ENABLEUPNPMASK
) ) {
948 syslog(LOG_ERR
, "Why did you run me anyway?");
952 if(GETFLAG(ENABLEUPNPMASK
)/*enableupnp*/)
955 /* open socket for HTTP connections. Listen on the 1st LAN address */
956 shttpl
= OpenAndConfHTTPSocket((v
.port
> 0) ? v
.port
: 0);
959 syslog(LOG_ERR
, "Failed to open socket for HTTP. EXITING");
963 struct sockaddr_in sockinfo
;
964 socklen_t len
= sizeof(struct sockaddr_in
);
965 if (getsockname(shttpl
, (struct sockaddr
*)&sockinfo
, &len
) < 0) {
966 syslog(LOG_ERR
, "getsockname(): %m");
969 v
.port
= ntohs(sockinfo
.sin_port
);
971 syslog(LOG_NOTICE
, "HTTP listening on port %d", v
.port
);
973 /* open socket for SSDP connections */
974 sudp
= OpenAndConfSSDPReceiveSocket(n_lan_addr
, lan_addr
);
977 /*syslog(LOG_ERR, "Failed to open socket for receiving SSDP. EXITING");
979 syslog(LOG_INFO
, "Failed to open socket for receiving SSDP. Trying to use MiniSSDPd");
980 if(SubmitServicesToMiniSSDPD(lan_addr
[0].str
, v
.port
) < 0) {
981 syslog(LOG_ERR
, "Failed to connect to MiniSSDPd. EXITING");
986 /* open socket for sending notifications */
987 if(OpenAndConfSSDPNotifySockets(snotify
) < 0)
989 syslog(LOG_ERR
, "Failed to open sockets for sending SSDP notify "
990 "messages. EXITING");
996 /* open socket for NAT PMP traffic */
997 if(GETFLAG(ENABLENATPMPMASK
))
999 snatpmp
= OpenAndConfNATPMPSocket();
1002 syslog(LOG_ERR
, "Failed to open socket for NAT PMP.");
1003 /*syslog(LOG_ERR, "Failed to open socket for NAT PMP. EXITING");
1006 syslog(LOG_NOTICE
, "Listening for NAT-PMP traffic on port %u",
1009 ScanNATPMPforExpiration();
1013 /* for miniupnpdctl */
1014 #ifdef USE_MINIUPNPDCTL
1015 sctl
= OpenAndConfCtlUnixSocket("/var/run/miniupnpd.ctl");
1018 tomato_helper(); // zzz
1023 /* Check if we need to send SSDP NOTIFY messages and do it if
1025 if(gettimeofday(&timeofday
, 0) < 0)
1027 syslog(LOG_ERR
, "gettimeofday(): %m");
1028 timeout
.tv_sec
= v
.notify_interval
;
1029 timeout
.tv_usec
= 0;
1033 /* the comparaison is not very precise but who cares ? */
1034 if(timeofday
.tv_sec
>= (lasttimeofday
.tv_sec
+ v
.notify_interval
))
1036 if (GETFLAG(ENABLEUPNPMASK
))
1037 SendSSDPNotifies2(snotify
,
1038 (unsigned short)v
.port
,
1039 v
.notify_interval
<< 1);
1040 memcpy(&lasttimeofday
, &timeofday
, sizeof(struct timeval
));
1041 timeout
.tv_sec
= v
.notify_interval
;
1042 timeout
.tv_usec
= 0;
1046 timeout
.tv_sec
= lasttimeofday
.tv_sec
+ v
.notify_interval
1048 if(timeofday
.tv_usec
> lasttimeofday
.tv_usec
)
1050 timeout
.tv_usec
= 1000000 + lasttimeofday
.tv_usec
1051 - timeofday
.tv_usec
;
1056 timeout
.tv_usec
= lasttimeofday
.tv_usec
- timeofday
.tv_usec
;
1060 /* remove unused rules */
1061 if( v
.clean_ruleset_interval
1062 && (timeofday
.tv_sec
>= checktime
.tv_sec
+ v
.clean_ruleset_interval
))
1066 remove_unused_rules(rule_list
);
1071 rule_list
= get_upnp_rules_state_list(v
.clean_ruleset_threshold
);
1073 memcpy(&checktime
, &timeofday
, sizeof(struct timeval
));
1075 #ifdef ENABLE_NATPMP
1076 /* Remove expired NAT-PMP mappings */
1077 while( nextnatpmptoclean_timestamp
&& (timeofday
.tv_sec
>= nextnatpmptoclean_timestamp
+ startup_time
))
1079 /*syslog(LOG_DEBUG, "cleaning expired NAT-PMP mappings");*/
1080 if(CleanExpiredNATPMP() < 0) {
1081 syslog(LOG_ERR
, "CleanExpiredNATPMP() failed");
1085 if(nextnatpmptoclean_timestamp
&& timeout
.tv_sec
>= (nextnatpmptoclean_timestamp
+ startup_time
- timeofday
.tv_sec
))
1087 /*syslog(LOG_DEBUG, "setting timeout to %d sec", nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec);*/
1088 timeout
.tv_sec
= nextnatpmptoclean_timestamp
+ startup_time
- timeofday
.tv_sec
;
1089 timeout
.tv_usec
= 0;
1093 /* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */
1098 FD_SET(sudp
, &readset
);
1099 max_fd
= MAX( max_fd
, sudp
);
1104 FD_SET(shttpl
, &readset
);
1105 max_fd
= MAX( max_fd
, shttpl
);
1108 i
= 0; /* active HTTP connections count */
1109 for(e
= upnphttphead
.lh_first
; e
!= NULL
; e
= e
->entries
.le_next
)
1111 if((e
->socket
>= 0) && (e
->state
<= 2))
1113 FD_SET(e
->socket
, &readset
);
1114 max_fd
= MAX( max_fd
, e
->socket
);
1122 syslog(LOG_DEBUG
, "%d active incoming HTTP connections", i
);
1125 #ifdef ENABLE_NATPMP
1127 FD_SET(snatpmp
, &readset
);
1128 max_fd
= MAX( max_fd
, snatpmp
);
1131 #ifdef USE_MINIUPNPDCTL
1133 FD_SET(sctl
, &readset
);
1134 max_fd
= MAX( max_fd
, sctl
);
1137 for(ectl
= ctllisthead
.lh_first
; ectl
; ectl
= ectl
->entries
.le_next
)
1139 if(ectl
->socket
>= 0) {
1140 FD_SET(ectl
->socket
, &readset
);
1141 max_fd
= MAX( max_fd
, ectl
->socket
);
1146 #ifdef ENABLE_EVENTS
1148 upnpevents_selectfds(&readset
, &writeset
, &max_fd
);
1151 #ifdef ENABLE_EVENTS
1152 if(select(max_fd
+1, &readset
, &writeset
, 0, &timeout
) < 0)
1154 if(select(max_fd
+1, &readset
, 0, 0, &timeout
) < 0)
1157 if(quitting
) goto shutdown
;
1158 if (gotusr2
) { // zzz
1163 syslog(LOG_ERR
, "select(all): %m");
1164 syslog(LOG_ERR
, "Failed to select open sockets. EXITING");
1165 return 1; /* very serious cause of error */
1167 #ifdef USE_MINIUPNPDCTL
1168 for(ectl
= ctllisthead
.lh_first
; ectl
;)
1170 ectlnext
= ectl
->entries
.le_next
;
1171 if((ectl
->socket
>= 0) && FD_ISSET(ectl
->socket
, &readset
))
1175 l
= read(ectl
->socket
, buf
, sizeof(buf
));
1178 /*write(ectl->socket, buf, l);*/
1179 write_option_list(ectl
->socket
);
1180 write_permlist(ectl
->socket
, upnppermlist
, num_upnpperm
);
1181 write_upnphttp_details(ectl
->socket
, upnphttphead
.lh_first
);
1182 write_ctlsockets_list(ectl
->socket
, ctllisthead
.lh_first
);
1183 write_ruleset_details(ectl
->socket
);
1184 #ifdef ENABLE_EVENTS
1185 write_events_details(ectl
->socket
);
1187 /* close the socket */
1188 close(ectl
->socket
);
1193 close(ectl
->socket
);
1197 if(ectl
->socket
< 0)
1199 LIST_REMOVE(ectl
, entries
);
1204 if((sctl
>= 0) && FD_ISSET(sctl
, &readset
))
1207 struct sockaddr_un clientname
;
1208 struct ctlelem
* tmp
;
1209 socklen_t clientnamelen
= sizeof(struct sockaddr_un
);
1210 //syslog(LOG_DEBUG, "sctl!");
1211 s
= accept(sctl
, (struct sockaddr
*)&clientname
,
1213 syslog(LOG_DEBUG
, "sctl! : '%s'", clientname
.sun_path
);
1214 tmp
= malloc(sizeof(struct ctlelem
));
1216 LIST_INSERT_HEAD(&ctllisthead
, tmp
, entries
);
1219 #ifdef ENABLE_EVENTS
1220 upnpevents_processfds(&readset
, &writeset
);
1222 #ifdef ENABLE_NATPMP
1223 /* process NAT-PMP packets */
1224 if((snatpmp
>= 0) && FD_ISSET(snatpmp
, &readset
))
1226 ProcessIncomingNATPMPPacket(snatpmp
);
1229 /* process SSDP packets */
1230 if(sudp
>= 0 && FD_ISSET(sudp
, &readset
))
1232 /*syslog(LOG_INFO, "Received UDP Packet");*/
1233 ProcessSSDPRequest(sudp
, (unsigned short)v
.port
);
1235 /* process active HTTP connections */
1236 /* LIST_FOREACH macro is not available under linux */
1237 for(e
= upnphttphead
.lh_first
; e
!= NULL
; e
= e
->entries
.le_next
)
1239 if( (e
->socket
>= 0) && (e
->state
<= 2)
1240 &&(FD_ISSET(e
->socket
, &readset
)) )
1242 Process_upnphttp(e
);
1245 /* process incoming HTTP connections */
1246 if(shttpl
>= 0 && FD_ISSET(shttpl
, &readset
))
1249 socklen_t clientnamelen
;
1250 struct sockaddr_in clientname
;
1251 clientnamelen
= sizeof(struct sockaddr_in
);
1252 shttp
= accept(shttpl
, (struct sockaddr
*)&clientname
, &clientnamelen
);
1255 syslog(LOG_ERR
, "accept(http): %m");
1259 struct upnphttp
* tmp
= 0;
1260 syslog(LOG_INFO
, "HTTP connection from %s:%d",
1261 inet_ntoa(clientname
.sin_addr
),
1262 ntohs(clientname
.sin_port
) );
1263 /*if (fcntl(shttp, F_SETFL, O_NONBLOCK) < 0) {
1264 syslog(LOG_ERR, "fcntl F_SETFL, O_NONBLOCK");
1266 /* Create a new upnphttp object and add it to
1267 * the active upnphttp object list */
1268 tmp
= New_upnphttp(shttp
);
1271 tmp
->clientaddr
= clientname
.sin_addr
;
1272 LIST_INSERT_HEAD(&upnphttphead
, tmp
, entries
);
1276 syslog(LOG_ERR
, "New_upnphttp() failed");
1281 /* delete finished HTTP connections */
1282 for(e
= upnphttphead
.lh_first
; e
!= NULL
; )
1284 next
= e
->entries
.le_next
;
1287 LIST_REMOVE(e
, entries
);
1295 tomato_save("/etc/upnp/data"); // zzz
1297 /* close out open sockets */
1298 while(upnphttphead
.lh_first
!= NULL
)
1300 e
= upnphttphead
.lh_first
;
1301 LIST_REMOVE(e
, entries
);
1305 if (sudp
>= 0) close(sudp
);
1306 if (shttpl
>= 0) close(shttpl
);
1307 #ifdef ENABLE_NATPMP
1314 #ifdef USE_MINIUPNPDCTL
1319 if(unlink("/var/run/miniupnpd.ctl") < 0)
1321 syslog(LOG_ERR
, "unlink() %m");
1326 /*if(SendSSDPGoodbye(snotify, v.n_lan_addr) < 0)*/
1327 if (GETFLAG(ENABLEUPNPMASK
))
1329 if(SendSSDPGoodbye(snotify
, n_lan_addr
) < 0)
1331 syslog(LOG_ERR
, "Failed to broadcast good-bye notifications");
1333 for(i
=0; i
<n_lan_addr
; i
++)/* for(i=0; i<v.n_lan_addr; i++)*/
1337 if(unlink(pidfilename
) < 0)
1339 syslog(LOG_ERR
, "Failed to remove pidfile %s: %m", pidfilename
);