int readoptionsfile(const char * fname) { FILE *hfile = NULL; char buffer[1024]; char *equals; char *name; char *value; char *t; int linenum = 0; int i; enum upnpconfigoptions id; if(!fname || (strlen(fname) == 0)) return -1; memset(buffer, 0, sizeof(buffer)); #ifdef DEBUG printf("Reading configuration from file %s\n", fname); #endif if(!(hfile = fopen(fname, "r"))) return -1; if(ary_options != NULL) { free(ary_options); num_options = 0; } while(fgets(buffer, sizeof(buffer), hfile)) { linenum++; t = strchr(buffer, '\n'); if(t) { *t = '\0'; t--; while(isspace(*t)) { *t = '\0'; t--; } } /* skip leading whitespaces */ name = buffer; while(isspace(*name)) name++; /* check for comments or empty lines */ if(name[0] == '#' || name[0] == '\0') continue; /* check for UPnP permissions rule */ if(0 == memcmp(name, "allow", 5) || 0 == memcmp(name, "deny", 4)) { upnppermlist = realloc(upnppermlist, sizeof(struct upnpperm) * (num_upnpperm+1)); /* parse the rule */ if(read_permission_line(upnppermlist + num_upnpperm, name) >= 0) { num_upnpperm++; } else { fprintf(stderr, "parsing error file %s line %d : %s\n", fname, linenum, name); } continue; } if(!(equals = strchr(name, '='))) { fprintf(stderr, "parsing error file %s line %d : %s\n", fname, linenum, name); continue; } /* remove ending whitespaces */ for(t=equals-1; t>name && isspace(*t); t--) *t = '\0'; *equals = '\0'; value = equals+1; /* skip leading whitespaces */ while(isspace(*value)) value++; id = UPNP_INVALID; for(i=0; i<sizeof(optionids)/sizeof(optionids[0]); i++) { /*printf("%2d %2d %s %s\n", i, optionids[i].id, name, optionids[i].name); */ if(0 == strcmp(name, optionids[i].name)) { id = optionids[i].id; break; } } if(id == UPNP_INVALID) { fprintf(stderr, "parsing error file %s line %d : %s=%s\n", fname, linenum, name, value); } else { num_options += 1; ary_options = (struct option *) realloc(ary_options, num_options * sizeof(struct option)); ary_options[num_options-1].id = id; strncpy(ary_options[num_options-1].value, value, MAX_OPTION_VALUE_LEN); } } fclose(hfile); return 0; }
int readoptionsfile(const char * fname) { FILE *hfile = NULL; char buffer[1024]; char *equals; char *name; char *value; char *t; int linenum = 0; unsigned int i; enum upnpconfigoptions id; size_t string_repo_len = 0; size_t len; void *tmp; if(!fname || (strlen(fname) == 0)) return -1; memset(buffer, 0, sizeof(buffer)); #ifdef DEBUG printf("Reading configuration from file %s\n", fname); #endif if(!(hfile = fopen(fname, "r"))) return -1; if(ary_options != NULL) { free(ary_options); num_options = 0; } while(fgets(buffer, sizeof(buffer), hfile)) { linenum++; t = strchr(buffer, '\n'); if(t) { *t = '\0'; t--; /* remove spaces at the end of the line */ while((t >= buffer) && isspace(*t)) { *t = '\0'; t--; } } /* skip leading whitespaces */ name = buffer; while(isspace(*name)) name++; /* check for comments or empty lines */ if(name[0] == '#' || name[0] == '\0') continue; len = strlen(name); /* length of the whole line excluding leading * and ending white spaces */ /* check for UPnP permissions rule */ if((len > 6) && (0 == memcmp(name, "allow", 5) || 0 == memcmp(name, "deny", 4))) { tmp = realloc(upnppermlist, sizeof(struct upnpperm) * (num_upnpperm+1)); if(tmp == NULL) { fprintf(stderr, "memory allocation error. Permission line in file %s line %d\n", fname, linenum); } else { upnppermlist = tmp; /* parse the rule */ if(read_permission_line(upnppermlist + num_upnpperm, name) >= 0) { num_upnpperm++; } else { fprintf(stderr, "parsing error file %s line %d : %s\n", fname, linenum, name); } } continue; } #ifdef PCP_SADSCP /* check for DSCP values configuration */ if((len > 15) && 0 == memcmp(name, "set_learn_dscp", sizeof("set_learn_dscp")-1) ) { tmp = realloc(dscp_values_list, sizeof(struct dscp_values) * (num_dscp_values+1)); if(tmp == NULL) { fprintf(stderr, "memory allocation error. DSCP line in file %s line %d\n", fname, linenum); } else { dscp_values_list = tmp; /* parse the rule */ if(read_learn_dscp_line(dscp_values_list + num_dscp_values, name) >= 0) { num_dscp_values++; } else { fprintf(stderr, "parsing error file %s line %d : %s\n", fname, linenum, name); } } continue; } #endif /* PCP_SADSCP */ if(!(equals = strchr(name, '='))) { fprintf(stderr, "parsing error file %s line %d : %s\n", fname, linenum, name); continue; } /* remove ending whitespaces */ for(t=equals-1; t>name && isspace(*t); t--) *t = '\0'; *equals = '\0'; value = equals+1; /* skip leading whitespaces */ while(isspace(*value)) value++; id = UPNP_INVALID; for(i=0; i<sizeof(optionids)/sizeof(optionids[0]); i++) { /*printf("%2d %2d %s %s\n", i, optionids[i].id, name, optionids[i].name); */ if(0 == strcmp(name, optionids[i].name)) { id = optionids[i].id; break; } } if(id == UPNP_INVALID) { fprintf(stderr, "invalid option in file %s line %d : %s=%s\n", fname, linenum, name, value); } else { tmp = realloc(ary_options, (num_options + 1) * sizeof(struct option)); if(tmp == NULL) { fprintf(stderr, "memory allocation error. Option in file %s line %d.\n", fname, linenum); } else { ary_options = tmp; len = strlen(value) + 1; /* +1 for terminating '\0' */ tmp = realloc(string_repo, string_repo_len + len); if(tmp == NULL) { fprintf(stderr, "memory allocation error, Option value in file %s line %d : %s=%s\n", fname, linenum, name, value); } else { string_repo = tmp; memcpy(string_repo + string_repo_len, value, len); ary_options[num_options].id = id; /* save the offset instead of the absolute address because realloc() could * change it */ ary_options[num_options].value = (const char *)string_repo_len; num_options += 1; string_repo_len += len; } } } } fclose(hfile); for(i = 0; i < num_options; i++) { /* add start address of string_repo to get right pointer */ ary_options[i].value = string_repo + (size_t)ary_options[i].value; } return 0; }
/* 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 * 8) set signal handlers */ static int init(int argc, char * * argv, struct runtime_vars * v) { int i; int pid; int debug_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; i<argc; i++) { if(0 == strcmp(argv[i], "-h")) goto print_usage; } #ifndef DISABLE_CONFIG_FILE /* 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; } } #endif /* DISABLE_CONFIG_FILE */ /* set initial values */ SETFLAG(ENABLEUPNPMASK); /* UPnP is enabled by default */ 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) { /* 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 { for(i=0; i<(int)num_options; i++) { switch(ary_options[i].id) { case UPNPEXT_IFNAME: ext_if_name = ary_options[i].value; break; case UPNPEXT_IP: use_ext_ip_addr = ary_options[i].value; break; 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) { 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); break; 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; 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; break; case UPNPNATCHAIN: miniupnpd_nat_chain = ary_options[i].value; break; #endif 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; #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); break; case UPNPSERIAL: strncpy(serialnumber, ary_options[i].value, SERIALNUMBER_MAX_LEN); serialnumber[SERIALNUMBER_MAX_LEN-1] = '\0'; break; 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 case UPNPANCHOR: anchor_name = ary_options[i].value; break; case UPNPQUEUE: queue = ary_options[i].value; break; case UPNPTAG: tag = ary_options[i].value; break; #endif #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; #endif #ifdef PF_ENABLE_FILTER_RULES case UPNPQUICKRULES: if(strcmp(ary_options[i].value, "no") == 0) SETFLAG(PFNOQUICKRULESMASK); break; #endif 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; #endif case UPNPMINISSDPDSOCKET: minissdpdsocketpath = ary_options[i].value; break; default: fprintf(stderr, "Unknown option in file %s\n", optionsfile); } } } #endif /* DISABLE_CONFIG_FILE */ /* 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]) { case 'o': if(i+1 < argc) use_ext_ip_addr = argv[++i]; else 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; 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; 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); 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; #endif case 'U': /*sysuptime = 1;*/ SETFLAG(SYSUPTIMEMASK); break; /*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; case 'i': if(i+1 < argc) ext_if_name = argv[++i]; else fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); break; #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; #endif case 'p': if(i+1 < argc) v->port = atoi(argv[++i]); else #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; #endif fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); break; case 'P': if(i+1 < argc) pidfilename = argv[++i]; else fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); break; 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': #ifndef MULTIPLE_EXTERNAL_IP 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) { fprintf(stderr, "can't parse \"%s\" as valid lan address\n", argv[i]); 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]); #else if(i+2 < argc) { char *val=calloc((strlen(argv[i+1]) + strlen(argv[i+2]) + 1), sizeof(char)); 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) { 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': i++; /* discarding, the config file is already read */ break; default: fprintf(stderr, "Unknown option: %s\n", argv[i]); } } if(!ext_if_name || !lan_addrs.lh_first) { /* bad configuration */ goto print_usage; } if(debug_flag) { pid = getpid(); } else { #ifdef USE_DAEMON if(daemon(0, 0)<0) { perror("daemon()"); } pid = getpid(); #else pid = daemonize(); #endif } 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)); } if(checkforrunning(pidfilename) < 0) { syslog(LOG_ERR, "MiniUPnPd is already running. EXITING"); return 1; } set_startup_time(GETFLAG(SYSUPTIMEMASK)); /* 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; if(sigaction(SIGTERM, &sa, NULL) < 0) { syslog(LOG_ERR, "Failed to set %s handler. EXITING", "SIGTERM"); return 1; } if(sigaction(SIGINT, &sa, NULL) < 0) { syslog(LOG_ERR, "Failed to set %s handler. EXITING", "SIGINT"); return 1; } 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) < 0) { syslog(LOG_NOTICE, "Failed to set %s handler", "SIGUSR1"); } if(init_redirect() < 0) { syslog(LOG_ERR, "Failed to init redirection engine. EXITING"); return 1; } #ifdef ENABLE_6FC_SERVICE #ifdef USE_NETFILTER init_iptpinhole(); #endif #endif if(writepidfile(pidfilename, pid) < 0) pidfilename = NULL; #ifdef ENABLE_LEASEFILE /*remove(lease_file);*/ syslog(LOG_INFO, "Reloading rules from lease file"); reload_from_lease_file(); #endif return 0; print_usage: fprintf(stderr, "Usage:\n\t" "%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 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] [-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 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" #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 "\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; }