--- embedaddon/miniupnpd/miniupnpd.c 2012/02/21 23:16:02 1.1 +++ embedaddon/miniupnpd/miniupnpd.c 2013/07/22 00:32:35 1.1.1.3 @@ -1,7 +1,7 @@ -/* $Id: miniupnpd.c,v 1.1 2012/02/21 23:16:02 misho Exp $ */ +/* $Id: miniupnpd.c,v 1.1.1.3 2013/07/22 00:32:35 misho Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2010 Thomas Bernard + * (c) 2006-2013 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -12,7 +12,9 @@ /* apt-get install libnetfilter-queue-dev */ #include #include -//#include /* Defines verdicts (NF_ACCEPT, etc) */ +#if 0 +#include /* Defines verdicts (NF_ACCEPT, etc) */ +#endif #include #include #include @@ -47,6 +49,7 @@ #include #endif +#include "macros.h" #include "upnpglobalvars.h" #include "upnphttp.h" #include "upnpdescgen.h" @@ -56,6 +59,7 @@ #include "options.h" #include "minissdp.h" #include "upnpredirect.h" +#include "upnppinhole.h" #include "miniupnpdtypes.h" #include "daemonize.h" #include "upnpevents.h" @@ -63,6 +67,15 @@ #include "natpmp.h" #endif #include "commonrdr.h" +#include "upnputils.h" +#ifdef USE_IFACEWATCHER +#include "ifacewatcher.h" +#endif +#ifdef ENABLE_6FC_SERVICE +#ifdef USE_NETFILTER +void init_iptpinhole(void); +#endif +#endif #ifndef DEFAULT_CONFIG #define DEFAULT_CONFIG "/etc/miniupnpd.conf" @@ -81,22 +94,15 @@ static struct nfq_handle *nfqHandle; static struct sockaddr_in ssdp; /* prototypes */ -void ProcessSSDPData(int s, char *bufr, struct sockaddr_in sendername, int n, unsigned short port); 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); #endif +/* variables used by signals */ +static volatile sig_atomic_t quitting = 0; +volatile sig_atomic_t should_send_public_address_change_notif = 0; -static int sudp = -1; - -/* MAX_LAN_ADDR : maximum number of interfaces - * to listen to SSDP traffic */ -/*#define MAX_LAN_ADDR (4)*/ - -static volatile int quitting = 0; -static volatile int should_send_public_address_change_notif = 0; - /* OpenAndConfHTTPSocket() : * setup the socket used to handle incoming HTTP connections. */ static int @@ -104,9 +110,20 @@ OpenAndConfHTTPSocket(unsigned short port) { int s; int i = 1; +#ifdef ENABLE_IPV6 + struct sockaddr_in6 listenname; +#else struct sockaddr_in listenname; +#endif + socklen_t listenname_len; - if( (s = socket(PF_INET, SOCK_STREAM, 0)) < 0) + if( (s = socket( +#ifdef ENABLE_IPV6 + PF_INET6, +#else + PF_INET, +#endif + SOCK_STREAM, 0)) < 0) { syslog(LOG_ERR, "socket(http): %m"); return -1; @@ -116,20 +133,41 @@ OpenAndConfHTTPSocket(unsigned short port) { 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 - memset(&listenname, 0, sizeof(struct sockaddr_in)); + if(!set_non_blocking(s)) + { + syslog(LOG_WARNING, "set_non_blocking(http): %m"); + } + +#ifdef ENABLE_IPV6 + memset(&listenname, 0, sizeof(struct sockaddr_in6)); + listenname.sin6_family = AF_INET6; + listenname.sin6_port = htons(port); + listenname.sin6_addr = in6addr_any; + listenname_len = sizeof(struct sockaddr_in6); +#else listenname.sin_family = AF_INET; listenname.sin_port = htons(port); listenname.sin_addr.s_addr = htonl(INADDR_ANY); + listenname_len = sizeof(struct sockaddr_in); +#endif - if(bind(s, (struct sockaddr *)&listenname, sizeof(struct sockaddr_in)) < 0) + if(bind(s, (struct sockaddr *)&listenname, listenname_len) < 0) { syslog(LOG_ERR, "bind(http): %m"); close(s); return -1; } - if(listen(s, 6) < 0) + if(listen(s, 5) < 0) { syslog(LOG_ERR, "listen(http): %m"); close(s); @@ -169,32 +207,32 @@ OpenAndConfNFqueue(){ inet_pton(AF_INET, "239.255.255.250", &(ssdp.sin_addr)); - //Get a queue connection handle from the module + /* Get a queue connection handle from the module */ if (!(nfqHandle = nfq_open())) { syslog(LOG_ERR, "Error in nfq_open(): %m"); return -1; } - //Unbind the handler from processing any IP packets - // Not totally sure why this is done, or if it's necessary... + /* Unbind the handler from processing any IP packets + Not totally sure why this is done, or if it's necessary... */ if ((e = nfq_unbind_pf(nfqHandle, AF_INET)) < 0) { syslog(LOG_ERR, "Error in nfq_unbind_pf(): %m"); return -1; } - //Bind this handler to process IP packets... + /* Bind this handler to process IP packets... */ if (nfq_bind_pf(nfqHandle, AF_INET) < 0) { syslog(LOG_ERR, "Error in nfq_bind_pf(): %m"); return -1; } - // Install a callback on queue -Q + /* Install a callback on queue -Q */ if (!(myQueue = nfq_create_queue(nfqHandle, nfqueue, &nfqueue_cb, NULL))) { syslog(LOG_ERR, "Error in nfq_create_queue(): %m"); return -1; } - // Turn on packet copy mode + /* Turn on packet copy mode */ if (nfq_set_mode(myQueue, NFQNL_COPY_PACKET, 0xffff) < 0) { syslog(LOG_ERR, "Error setting packet copy mode (): %m"); return -1; @@ -232,13 +270,13 @@ static int nfqueue_cb( int dport = get_udp_dst_port(pkt); int x = sizeof (struct ip) + sizeof (struct udphdr); - - /* packets we are interested in are UDP multicast to 239.255.255.250:1900 + + /* packets we are interested in are UDP multicast to 239.255.255.250:1900 * and start with a data string M-SEARCH */ - if ( (dport == 1900) && (id_protocol == IPPROTO_UDP) + if ( (dport == 1900) && (id_protocol == IPPROTO_UDP) && (ssdp.sin_addr.s_addr == iph->ip_dst.s_addr) ) { - + /* get the index that the packet came in on */ u_int32_t idx = nfq_get_indev(nfa); int i = 0; @@ -248,23 +286,25 @@ static int nfqueue_cb( struct udphdr *udp = (struct udphdr *) (pkt + sizeof(struct ip)); char *dd = pkt + x; - + 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, sendername, size - x, (unsigned short) 5555); + ProcessSSDPData (sudp, dd, size - x, + &sendername, (unsigned short) 5555); } } } - + nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL); } else { syslog(LOG_ERR,"nfq_get_msg_packet_hdr failed"); - return 1; // from nfqueue source: 0 = ok, >0 = soft error, <0 hard error + return 1; + /* from nfqueue source: 0 = ok, >0 = soft error, <0 hard error */ } return 0; @@ -344,12 +384,13 @@ write_ctlsockets_list(int fd, struct ctlelem * e) } } +#ifndef DISABLE_CONFIG_FILE static void write_option_list(int fd) { char buffer[256]; int len; - int i; + unsigned int i; write(fd, "Options :\n", 10); for(i=0; iifname)) { - nbits = atoi(++p); - while(*p && !isspace(*p)) - p++; + /* not starting with a digit : suppose it is an interface name */ + memcpy(lan_addr->ifname, str, n); + lan_addr->ifname[n] = '\0'; + if(getifaddr(lan_addr->ifname, lan_addr->str, sizeof(lan_addr->str)) < 0) + goto parselan_error; } - if(n>15) + else { - fprintf(stderr, "Error parsing address/mask : %s\n", str); - return -1; + if(n>15) + goto parselan_error; + memcpy(lan_addr->str, str, n); + lan_addr->str[n] = '\0'; } - memcpy(lan_addr->str, str, n); - lan_addr->str[n] = '\0'; if(!inet_aton(lan_addr->str, &lan_addr->addr)) + goto parselan_error; + if(*p == '/') { - fprintf(stderr, "Error parsing address/mask : %s\n", str); - return -1; + const char * q = ++p; + while(*p && isdigit(*p)) + p++; + if(*p=='.') + { + while(*p && (*p=='.' || isdigit(*p))) + p++; + n = p - q; + if(n>15) + goto parselan_error; + memcpy(tmp, q, n); + tmp[n] = '\0'; + if(!inet_aton(tmp, &lan_addr->mask)) + goto parselan_error; + } + else + { + int nbits = atoi(q); + if(nbits > 32 || nbits < 0) + goto parselan_error; + lan_addr->mask.s_addr = htonl(nbits ? (0xffffffffu << (32 - nbits)) : 0); + } } - lan_addr->mask.s_addr = htonl(nbits ? (0xffffffff << (32 - nbits)) : 0); + else + { + /* by default, networks are /24 */ + lan_addr->mask.s_addr = htonl(0xffffff00u); + } #ifdef MULTIPLE_EXTERNAL_IP /* skip spaces */ while(*p && isspace(*p)) @@ -539,7 +622,20 @@ parselanaddr(struct lan_addr_s * lan_addr, const char } } #endif +#ifdef ENABLE_IPV6 + if(lan_addr->ifname[0] != '\0') + { + lan_addr->index = if_nametoindex(lan_addr->ifname); + if(lan_addr->index == 0) + fprintf(stderr, "Cannot get index for network interface %s", + lan_addr->ifname); + } +#endif return 0; +parselan_error: + fprintf(stderr, "Error parsing address/mask (or interface name) : %s\n", + str); + return -1; } /* init phase : @@ -557,12 +653,16 @@ init(int argc, char * * argv, struct runtime_vars * v) int i; int pid; int debug_flag = 0; - int options_flag = 0; int openlog_option; struct sigaction sa; /*const char * logfilename = 0;*/ const char * presurl = 0; +#ifndef DISABLE_CONFIG_FILE + int options_flag = 0; const char * optionsfile = DEFAULT_CONFIG; +#endif /* DISABLE_CONFIG_FILE */ + struct lan_addr_s * lan_addr; + struct lan_addr_s * lan_addr2; /* only print usage if -h is used */ for(i=1; in_lan_addr = 0;*/ + LIST_INIT(&lan_addrs); v->port = -1; v->notify_interval = 30; /* seconds between SSDP announces */ v->clean_ruleset_threshold = 20; v->clean_ruleset_interval = 0; /* interval between ruleset check. 0=disabled */ - +#ifndef DISABLE_CONFIG_FILE /* read options file first since * command line arguments have final say */ if(readoptionsfile(optionsfile) < 0) @@ -600,7 +702,7 @@ init(int argc, char * * argv, struct runtime_vars * v) } else { - for(i=0; in_lan_addr < MAX_LAN_ADDR)*/ + lan_addr = (struct lan_addr_s *) malloc(sizeof(struct lan_addr_s)); + if (lan_addr == NULL) { - /*if(parselanaddr(&v->lan_addr[v->n_lan_addr],*/ - if(parselanaddr(&lan_addr[n_lan_addr], - ary_options[i].value) == 0) - n_lan_addr++; /*v->n_lan_addr++; */ + fprintf(stderr, "malloc(sizeof(struct lan_addr_s)): %m"); + break; } - else + if(parselanaddr(lan_addr, ary_options[i].value) != 0) { - fprintf(stderr, "Too many listening ips (max: %d), ignoring %s\n", - MAX_LAN_ADDR, ary_options[i].value); + fprintf(stderr, "can't parse \"%s\" as valid lan address\n", ary_options[i].value); + free(lan_addr); + break; } + LIST_INSERT_HEAD(&lan_addrs, lan_addr, list); break; case UPNPPORT: v->port = atoi(ary_options[i].value); @@ -636,6 +739,10 @@ init(int argc, char * * argv, struct runtime_vars * v) case UPNPPRESENTATIONURL: presurl = ary_options[i].value; break; + case UPNPFRIENDLY_NAME: + strncpy(friendly_name, ary_options[i].value, FRIENDLY_NAME_MAX_LEN); + friendly_name[FRIENDLY_NAME_MAX_LEN-1] = '\0'; + break; #ifdef USE_NETFILTER case UPNPFORWARDCHAIN: miniupnpd_forward_chain = ary_options[i].value; @@ -651,10 +758,12 @@ init(int argc, char * * argv, struct runtime_vars * v) if(strcmp(ary_options[i].value, "yes") == 0) SETFLAG(SYSUPTIMEMASK); /*sysuptime = 1;*/ break; +#if defined(USE_PF) || defined(USE_IPF) case UPNPPACKET_LOG: if(strcmp(ary_options[i].value, "yes") == 0) SETFLAG(LOGPACKETSMASK); /*logpackets = 1;*/ break; +#endif case UPNPUUID: strncpy(uuidvalue+5, ary_options[i].value, strlen(uuidvalue+5) + 1); @@ -662,7 +771,7 @@ init(int argc, char * * argv, struct runtime_vars * v) case UPNPSERIAL: strncpy(serialnumber, ary_options[i].value, SERIALNUMBER_MAX_LEN); serialnumber[SERIALNUMBER_MAX_LEN-1] = '\0'; - break; + break; case UPNPMODEL_NUMBER: strncpy(modelnumber, ary_options[i].value, MODELNUMBER_MAX_LEN); modelnumber[MODELNUMBER_MAX_LEN-1] = '\0'; @@ -674,6 +783,9 @@ init(int argc, char * * argv, struct runtime_vars * v) v->clean_ruleset_interval = atoi(ary_options[i].value); break; #ifdef USE_PF + case UPNPANCHOR: + anchor_name = ary_options[i].value; + break; case UPNPQUEUE: queue = ary_options[i].value; break; @@ -719,6 +831,7 @@ init(int argc, char * * argv, struct runtime_vars * v) } } } +#endif /* DISABLE_CONFIG_FILE */ /* command line arguments processing */ for(i=1; iclean_ruleset_interval = atoi(argv[++i]); + else + fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); + break; case 'u': if(i+1 < argc) strncpy(uuidvalue+5, argv[++i], strlen(uuidvalue+5) + 1); else fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); break; + 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; case 's': if(i+1 < argc) strncpy(serialnumber, argv[++i], SERIALNUMBER_MAX_LEN); @@ -774,10 +900,12 @@ init(int argc, char * * argv, struct runtime_vars * v) /*case 'l': logfilename = argv[++i]; break;*/ +#if defined(USE_PF) || defined(USE_IPF) case 'L': /*logpackets = 1;*/ SETFLAG(LOGPACKETSMASK); break; +#endif case 'S': SETFLAG(SECUREMODEMASK); break; @@ -854,32 +982,89 @@ init(int argc, char * * argv, struct runtime_vars * v) fprintf(stderr, "Option -%c takes two arguments.\n", argv[i][1]); break; case 'a': +#ifndef MULTIPLE_EXTERNAL_IP if(i+1 < argc) { - int address_already_there = 0; - int j; i++; - for(j=0; jlist.le_next) { - if(parselanaddr(&lan_addr[n_lan_addr], argv[i]) == 0) - n_lan_addr++; + if (0 == strncmp(lan_addr2->str, lan_addr->str, 15)) + break; } - else + if (lan_addr2 == NULL) + LIST_INSERT_HEAD(&lan_addrs, lan_addr, list); + } + else + fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); +#else + if(i+2 < argc) + { + char *val=calloc((strlen(argv[i+1]) + strlen(argv[i+2]) + 1), sizeof(char)); + if (val == NULL) { - fprintf(stderr, "Too many listening ips (max: %d), ignoring %s\n", - MAX_LAN_ADDR, argv[i]); + 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) + { + fprintf(stderr, "can't parse \"%s\" as valid lan address\n", val); + 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]); +#endif + break; + 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; case 'f': @@ -889,7 +1074,7 @@ init(int argc, char * * argv, struct runtime_vars * v) fprintf(stderr, "Unknown option: %s\n", argv[i]); } } - if(!ext_if_name || (n_lan_addr==0)) + if(!ext_if_name || !lan_addrs.lh_first) { /* bad configuration */ goto print_usage; @@ -929,7 +1114,7 @@ init(int argc, char * * argv, struct runtime_vars * v) { syslog(LOG_ERR, "MiniUPnPd is already running. EXITING"); return 1; - } + } set_startup_time(GETFLAG(SYSUPTIMEMASK)); @@ -942,31 +1127,31 @@ init(int argc, char * * argv, struct runtime_vars * v) else { snprintf(presentationurl, PRESENTATIONURL_MAX_LEN, - "http://%s/", lan_addr[0].str); - /*"http://%s:%d/", lan_addr[0].str, 80);*/ + "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; - if (sigaction(SIGTERM, &sa, NULL)) + if(sigaction(SIGTERM, &sa, NULL) < 0) { syslog(LOG_ERR, "Failed to set %s handler. EXITING", "SIGTERM"); return 1; } - if (sigaction(SIGINT, &sa, NULL)) + if(sigaction(SIGINT, &sa, NULL) < 0) { syslog(LOG_ERR, "Failed to set %s handler. EXITING", "SIGINT"); return 1; } - - if(signal(SIGPIPE, SIG_IGN) == SIG_ERR) { + sa.sa_handler = SIG_IGN; + if(sigaction(SIGPIPE, &sa, NULL) < 0) + { syslog(LOG_ERR, "Failed to ignore SIGPIPE signals"); } - sa.sa_handler = sigusr1; - if (sigaction(SIGUSR1, &sa, NULL)) + if(sigaction(SIGUSR1, &sa, NULL) < 0) { syslog(LOG_NOTICE, "Failed to set %s handler", "SIGUSR1"); } @@ -976,8 +1161,14 @@ init(int argc, char * * argv, struct runtime_vars * v) syslog(LOG_ERR, "Failed to init redirection engine. EXITING"); return 1; } +#ifdef ENABLE_6FC_SERVICE +#ifdef USE_NETFILTER + init_iptpinhole(); +#endif +#endif - writepidfile(pidfilename, pid); + if(writepidfile(pidfilename, pid) < 0) + pidfilename = NULL; #ifdef ENABLE_LEASEFILE /*remove(lease_file);*/ @@ -988,33 +1179,49 @@ init(int argc, char * * argv, struct runtime_vars * v) return 0; print_usage: fprintf(stderr, "Usage:\n\t" - "%s [-f config_file] [-i ext_ifname] [-o ext_ip]\n" -#ifndef ENABLE_NATPMP - "\t\t[-a listening_ip] [-p port] [-d] [-L] [-U] [-S]\n" + "%s " +#ifndef DISABLE_CONFIG_FILE + "[-f config_file] " +#endif + "[-i ext_ifname] [-o ext_ip]\n" +#ifndef MULTIPLE_EXTERNAL_IP + "\t\t[-a listening_ip]" #else - "\t\t[-a listening_ip] [-p port] [-d] [-L] [-U] [-S] [-N]\n" + "\t\t[-a listening_ip ext_ip]" #endif + " [-p port] [-d]" +#if defined(USE_PF) || defined(USE_IPF) + " [-L]" +#endif + " [-U] [-S]" +#ifdef ENABLE_NATPMP + " [-N]" +#endif + "\n" /*"[-l logfile] " not functionnal */ "\t\t[-u uuid] [-s serial] [-m model_number] \n" - "\t\t[-t notify_interval] [-P pid_filename]\n" - "\t\t[-B down up] [-w url]\n" + "\t\t[-t notify_interval] [-P pid_filename] [-z fiendly_name]\n" + "\t\t[-B down up] [-w url] [-r clean_ruleset_interval]\n" #ifdef USE_PF "\t\t[-q queue] [-T tag]\n" #endif #ifdef ENABLE_NFQUEUE "\t\t[-Q queue] [-n name]\n" #endif + "\t\t[-A \"permission rule\"]\n" "\nNotes:\n\tThere can be one or several listening_ips.\n" "\tNotify interval is in seconds. Default is 30 seconds.\n" "\tDefault pid file is '%s'.\n" "\tDefault config file is '%s'.\n" "\tWith -d miniupnpd will run as a standard program.\n" +#if defined(USE_PF) || defined(USE_IPF) "\t-L sets packet log in pf and ipf on.\n" +#endif "\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 - "\t-N enable NAT-PMP functionnality.\n" + "\t-N enable NAT-PMP functionality.\n" #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" @@ -1023,9 +1230,14 @@ print_usage: "\t-T sets the tag name in pf.\n" #endif #ifdef ENABLE_NFQUEUE - "\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" + "\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" #endif + "\t-A use following syntax for permission rules :\n" + "\t (allow|deny) (external port range) ip/mask (internal port range)\n" + "\texamples :\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" "\t-h prints this help and quits.\n" "", argv[0], pidfilename, DEFAULT_CONFIG); return 1; @@ -1037,21 +1249,28 @@ int main(int argc, char * * argv) { int i; - int shttpl = -1; + int shttpl = -1; /* socket for HTTP */ + 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 - int snatpmp[MAX_LAN_ADDR]; + int * snatpmp = NULL; +#endif #ifdef ENABLE_NFQUEUE int nfqh = -1; #endif +#ifdef USE_IFACEWATCHER + int sifacewatcher = -1; #endif - int snotify[MAX_LAN_ADDR]; + + int * snotify = NULL; + int addr_count; LIST_HEAD(httplisthead, upnphttp) upnphttphead; struct upnphttp * e = 0; struct upnphttp * next; fd_set readset; /* for select() */ -#ifdef ENABLE_EVENTS fd_set writeset; -#endif struct timeval timeout, timeofday, lasttimeofday = {0, 0}; int max_fd = -1; #ifdef USE_MINIUPNPDCTL @@ -1064,16 +1283,32 @@ main(int argc, char * * argv) /* variables used for the unused-rule cleanup process */ struct rule_state * rule_list = 0; struct timeval checktime = {0, 0}; - syslog(LOG_INFO, "SNet version started"); - - - memset(snotify, 0, sizeof(snotify)); -#ifdef ENABLE_NATPMP - for(i = 0; i < MAX_LAN_ADDR; i++) - snatpmp[i] = -1; + struct lan_addr_s * lan_addr; +#ifdef ENABLE_6FC_SERVICE + unsigned int next_pinhole_ts; #endif + if(init(argc, argv, &v) != 0) return 1; + /* 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++; + if(addr_count > 0) { +#ifndef ENABLE_IPV6 + snotify = calloc(addr_count, sizeof(int)); +#else + /* one for IPv4, one for IPv6 */ + snotify = calloc(addr_count * 2, sizeof(int)); +#endif + } +#ifdef ENABLE_NATPMP + if(addr_count > 0) { + snatpmp = malloc(addr_count * sizeof(int)); + for(i = 0; i < addr_count; i++) + snatpmp[i] = -1; + } +#endif LIST_INIT(&upnphttphead); #ifdef USE_MINIUPNPDCTL @@ -1089,6 +1324,15 @@ main(int argc, char * * argv) return 0; } + syslog(LOG_INFO, "Starting%s%swith external interface %s", +#ifdef ENABLE_NATPMP + GETFLAG(ENABLENATPMPMASK) ? " NAT-PMP " : " ", +#else + " ", +#endif + GETFLAG(ENABLEUPNPMASK) ? "UPnP-IGD " : "", + ext_if_name); + if(GETFLAG(ENABLEUPNPMASK)) { @@ -1109,17 +1353,33 @@ main(int argc, char * * argv) v.port = ntohs(sockinfo.sin_port); } syslog(LOG_NOTICE, "HTTP listening on port %d", v.port); +#ifdef ENABLE_IPV6 + if(find_ipv6_addr(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"); + } +#endif /* open socket for SSDP connections */ - sudp = OpenAndConfSSDPReceiveSocket(n_lan_addr, lan_addr); + sudp = OpenAndConfSSDPReceiveSocket(0); if(sudp < 0) { - syslog(LOG_INFO, "Failed to open socket for receiving SSDP. Trying to use MiniSSDPd"); - if(SubmitServicesToMiniSSDPD(lan_addr[0].str, v.port) < 0) { + syslog(LOG_NOTICE, "Failed to open socket for receiving SSDP. Trying to use MiniSSDPd"); + if(SubmitServicesToMiniSSDPD(lan_addrs.lh_first->str, v.port) < 0) { syslog(LOG_ERR, "Failed to connect to MiniSSDPd. EXITING"); return 1; } } +#ifdef ENABLE_IPV6 + sudpv6 = OpenAndConfSSDPReceiveSocket(1); + if(sudpv6 < 0) + { + syslog(LOG_WARNING, "Failed to open socket for receiving SSDP (IP v6)."); + } +#endif /* open socket for sending notifications */ if(OpenAndConfSSDPNotifySockets(snotify) < 0) @@ -1128,6 +1388,18 @@ main(int argc, char * * argv) "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 @@ -1141,7 +1413,9 @@ main(int argc, char * * argv) syslog(LOG_NOTICE, "Listening for NAT-PMP traffic on port %u", NATPMP_PORT); } +#if 0 ScanNATPMPforExpiration(); +#endif } #endif @@ -1161,6 +1435,7 @@ main(int argc, char * * argv) } } #endif + /* main loop */ while(!quitting) { @@ -1168,7 +1443,23 @@ main(int argc, char * * argv) if((startup_time<60*60*24) && (time(NULL)>60*60*24)) { set_startup_time(GETFLAG(SYSUPTIMEMASK)); - } + } + /* send public address change notifications if needed */ + if(should_send_public_address_change_notif) + { + syslog(LOG_DEBUG, "should send external iface address change notification(s)"); +#ifdef ENABLE_NATPMP + if(GETFLAG(ENABLENATPMPMASK)) + SendNATPMPPublicAddressChangeNotification(snatpmp, addr_count); +#endif +#ifdef ENABLE_EVENTS + if(GETFLAG(ENABLEUPNPMASK)) + { + upnp_event_var_change_notify(EWanIPC); + } +#endif + should_send_public_address_change_notif = 0; + } /* Check if we need to send SSDP NOTIFY messages and do it if * needed */ if(gettimeofday(&timeofday, 0) < 0) @@ -1221,9 +1512,27 @@ main(int argc, char * * argv) } memcpy(&checktime, &timeofday, sizeof(struct timeval)); } + /* Remove expired port mappings, based on UPnP IGD LeaseDuration + * or NAT-PMP lifetime) */ + if(nextruletoclean_timestamp + && ((unsigned int)timeofday.tv_sec >= nextruletoclean_timestamp)) + { + syslog(LOG_DEBUG, "cleaning expired Port Mappings"); + get_upnp_rules_state_list(0); + } + if(nextruletoclean_timestamp + && ((unsigned int)timeout.tv_sec >= (nextruletoclean_timestamp - timeofday.tv_sec))) + { + timeout.tv_sec = nextruletoclean_timestamp - timeofday.tv_sec; + timeout.tv_usec = 0; + syslog(LOG_DEBUG, "setting timeout to %u sec", + (unsigned)timeout.tv_sec); + } #ifdef ENABLE_NATPMP +#if 0 /* Remove expired NAT-PMP mappings */ - while( nextnatpmptoclean_timestamp && (timeofday.tv_sec >= nextnatpmptoclean_timestamp + startup_time)) + while(nextnatpmptoclean_timestamp + && (timeofday.tv_sec >= nextnatpmptoclean_timestamp + startup_time)) { /*syslog(LOG_DEBUG, "cleaning expired NAT-PMP mappings");*/ if(CleanExpiredNATPMP() < 0) { @@ -1231,44 +1540,76 @@ main(int argc, char * * argv) break; } } - if(nextnatpmptoclean_timestamp && timeout.tv_sec >= (nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec)) + if(nextnatpmptoclean_timestamp + && timeout.tv_sec >= (nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec)) { - /*syslog(LOG_DEBUG, "setting timeout to %d sec", nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec);*/ -#ifdef ENABLE_NFQUEUE - if (nfqh >= 0) - { - FD_SET(nfqh, &readset); - max_fd = MAX( max_fd, nfqh); + /*syslog(LOG_DEBUG, "setting timeout to %d sec", + nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec);*/ + timeout.tv_sec = nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec; + timeout.tv_usec = 0; } #endif - - timeout.tv_sec = nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec; +#endif +#ifdef ENABLE_6FC_SERVICE + /* Clean up expired IPv6 PinHoles */ + next_pinhole_ts = 0; + upnp_clean_expired_pinholes(&next_pinhole_ts); + if(next_pinhole_ts && + timeout.tv_sec >= (int)(next_pinhole_ts - timeofday.tv_sec)) { + timeout.tv_sec = next_pinhole_ts - timeofday.tv_sec; timeout.tv_usec = 0; } #endif /* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */ FD_ZERO(&readset); + FD_ZERO(&writeset); - if (sudp >= 0) + if (sudp >= 0) { 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 } - - if (shttpl >= 0) + if (shttpl >= 0) { FD_SET(shttpl, &readset); max_fd = MAX( max_fd, shttpl); } +#ifdef ENABLE_IPV6 + if (sudpv6 >= 0) + { + FD_SET(sudpv6, &readset); + max_fd = MAX( max_fd, sudpv6); + } +#endif +#ifdef ENABLE_NFQUEUE + if (nfqh >= 0) + { + 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) { - if((e->socket >= 0) && (e->state <= 2)) + if(e->socket >= 0) { - FD_SET(e->socket, &readset); - max_fd = MAX( max_fd, e->socket); + 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); i++; } } @@ -1280,7 +1621,7 @@ main(int argc, char * * argv) } #endif #ifdef ENABLE_NATPMP - for(i=0; i= 0) { FD_SET(snatpmp[i], &readset); max_fd = MAX( max_fd, snatpmp[i]); @@ -1292,7 +1633,7 @@ main(int argc, char * * argv) FD_SET(sctl, &readset); max_fd = MAX( max_fd, sctl); } - + for(ectl = ctllisthead.lh_first; ectl; ectl = ectl->entries.le_next) { if(ectl->socket >= 0) { @@ -1303,15 +1644,10 @@ main(int argc, char * * argv) #endif #ifdef ENABLE_EVENTS - FD_ZERO(&writeset); upnpevents_selectfds(&readset, &writeset, &max_fd); #endif -#ifdef ENABLE_EVENTS if(select(max_fd+1, &readset, &writeset, 0, &timeout) < 0) -#else - if(select(max_fd+1, &readset, 0, 0, &timeout) < 0) -#endif { if(quitting) goto shutdown; if(errno == EINTR) continue; /* interrupted by a signal, start again */ @@ -1332,7 +1668,9 @@ main(int argc, char * * argv) { /*write(ectl->socket, buf, l);*/ write_command_line(ectl->socket, argc, argv); +#ifndef DISABLE_CONFIG_FILE write_option_list(ectl->socket); +#endif write_permlist(ectl->socket, upnppermlist, num_upnpperm); write_upnphttp_details(ectl->socket, upnphttphead.lh_first); write_ctlsockets_list(ectl->socket, ctllisthead.lh_first); @@ -1363,13 +1701,21 @@ main(int argc, char * * argv) struct sockaddr_un clientname; struct ctlelem * tmp; socklen_t clientnamelen = sizeof(struct sockaddr_un); - //syslog(LOG_DEBUG, "sctl!"); + /*syslog(LOG_DEBUG, "sctl!");*/ s = accept(sctl, (struct sockaddr *)&clientname, &clientnamelen); syslog(LOG_DEBUG, "sctl! : '%s'", clientname.sun_path); tmp = malloc(sizeof(struct ctlelem)); - tmp->socket = s; - LIST_INSERT_HEAD(&ctllisthead, tmp, entries); + 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); + } } #endif #ifdef ENABLE_EVENTS @@ -1377,7 +1723,7 @@ main(int argc, char * * argv) #endif #ifdef ENABLE_NATPMP /* process NAT-PMP packets */ - for(i=0; i= 0) && FD_ISSET(snatpmp[i], &readset)) { @@ -1391,14 +1737,30 @@ main(int argc, char * * argv) /*syslog(LOG_INFO, "Received UDP Packet");*/ ProcessSSDPRequest(sudp, (unsigned short)v.port); } +#ifdef ENABLE_IPV6 + if(sudpv6 >= 0 && FD_ISSET(sudpv6, &readset)) + { + syslog(LOG_INFO, "Received UDP Packet (IPv6)"); + ProcessSSDPRequest(sudpv6, (unsigned short)v.port); + } +#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) { - if( (e->socket >= 0) && (e->state <= 2) - &&(FD_ISSET(e->socket, &readset)) ) + if(e->socket >= 0) { - Process_upnphttp(e); + if(FD_ISSET(e->socket, &readset) || + FD_ISSET(e->socket, &writeset)) + { + Process_upnphttp(e); + } } } /* process incoming HTTP connections */ @@ -1406,22 +1768,77 @@ main(int argc, char * * argv) { 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) { - syslog(LOG_ERR, "accept(http): %m"); + /* 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; - syslog(LOG_INFO, "HTTP connection from %s:%d", - inet_ntoa(clientname.sin_addr), - ntohs(clientname.sin_port) ); - /*if (fcntl(shttp, F_SETFL, O_NONBLOCK) < 0) { - syslog(LOG_ERR, "fcntl F_SETFL, O_NONBLOCK"); - }*/ + char addr_str[64]; + + sockaddr_to_string((struct sockaddr *)&clientname, addr_str, sizeof(addr_str)); + syslog(LOG_INFO, "HTTP connection from %s", addr_str); + if(get_lan_for_peer((struct sockaddr *)&clientname) == NULL) + { + /* The peer is not a LAN ! */ + syslog(LOG_WARNING, + "HTTP peer %s is not from a LAN, closing the connection", + addr_str); + 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 + LIST_INSERT_HEAD(&upnphttphead, tmp, entries); + } + else + { + syslog(LOG_ERR, "New_upnphttp() failed"); + close(shttp); + } + } + } + } #ifdef ENABLE_NFQUEUE /* process NFQ packets */ if(nfqh >= 0 && FD_ISSET(nfqh, &readset)) @@ -1430,26 +1847,11 @@ main(int argc, char * * argv) ProcessNFQUEUE(nfqh); } #endif - /* Create a new upnphttp object and add it to - * the active upnphttp object list */ - tmp = New_upnphttp(shttp); - if(tmp) - { - tmp->clientaddr = clientname.sin_addr; - LIST_INSERT_HEAD(&upnphttphead, tmp, entries); - } - else - { - syslog(LOG_ERR, "New_upnphttp() failed"); - close(shttp); - } - } - } /* delete finished HTTP connections */ for(e = upnphttphead.lh_first; e != NULL; ) { next = e->entries.le_next; - if(e->state >= 100) + if(e->state >= EToDelete) { LIST_REMOVE(e, entries); Delete_upnphttp(e); @@ -1457,21 +1859,6 @@ main(int argc, char * * argv) e = next; } - /* send public address change notifications */ - if(should_send_public_address_change_notif) - { -#ifdef ENABLE_NATPMP - if(GETFLAG(ENABLENATPMPMASK)) - SendNATPMPPublicAddressChangeNotification(snatpmp/*snotify*/, n_lan_addr); -#endif -#ifdef ENABLE_EVENTS - if(GETFLAG(ENABLEUPNPMASK)) - { - upnp_event_var_change_notify(EWanIPC); - } -#endif - should_send_public_address_change_notif = 0; - } } /* end of main loop */ shutdown: @@ -1485,8 +1872,14 @@ shutdown: if (sudp >= 0) close(sudp); if (shttpl >= 0) close(shttpl); +#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=0) { close(snatpmp[i]); @@ -1505,26 +1898,47 @@ shutdown: } } #endif - - /*if(SendSSDPGoodbye(snotify, v.n_lan_addr) < 0)*/ + if (GETFLAG(ENABLEUPNPMASK)) { - if(SendSSDPGoodbye(snotify, n_lan_addr) < 0) +#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"); } - for(i=0; i