📄 miniupnpd.c
字号:
/* $Id: miniupnpd.c,v 1.93 2007/11/02 23:56:56 nanard Exp $ *//* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2007 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */#include <stdlib.h>#include <unistd.h>#include <string.h>#include <stdio.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 <sys/param.h>#if defined(sun)#include <kstat.h>#else/* for BSD's sysctl */#include <sys/sysctl.h>#endif/* unix sockets *//*#define USE_MINIUPNPDCTL*/#include "config.h"#ifdef USE_MINIUPNPDCTL#include <sys/un.h>#endif#include "upnpglobalvars.h"#include "upnphttp.h"#include "upnpdescgen.h"#include "miniupnpdpath.h"#include "getifaddr.h"#include "daemonize.h"#include "upnpsoap.h"#include "options.h"#include "minissdp.h"#include "upnpredirect.h"#include "miniupnpdtypes.h"#ifdef ENABLE_NATPMP#include "natpmp.h"#endif#ifdef USE_MINIUPNPDCTLstruct ctlelem { int socket; LIST_ENTRY(ctlelem) entries;};#endif/* MAX_LAN_ADDR : maximum number of interfaces * to listen to SSDP traffic */#define MAX_LAN_ADDR (4)static volatile int quitting = 0;/* OpenAndConfHTTPSocket() : * setup the socket used to handle incoming HTTP connections. */static intOpenAndConfHTTPSocket(unsigned short port){ int s; int i = 1; struct sockaddr_in listenname; if( (s = socket(PF_INET, SOCK_STREAM, 0)) < 0) { 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"); } memset(&listenname, 0, sizeof(struct sockaddr_in)); listenname.sin_family = AF_INET; listenname.sin_port = htons(port); listenname.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(s, (struct sockaddr *)&listenname, sizeof(struct sockaddr_in)) < 0) { syslog(LOG_ERR, "bind(http): %m"); close(s); return -1; } if(listen(s, 6) < 0) { syslog(LOG_ERR, "listen(http): %m"); close(s); return -1; } return s;}/* Functions used to communicate with miniupnpdctl */#ifdef USE_MINIUPNPDCTLstatic intOpenAndConfCtlUnixSocket(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 voidwrite_upnphttp_details(int fd, struct upnphttp * e){ char buffer[256]; int len; 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 voidwrite_ctlsockets_list(int fd, struct ctlelem * e){ char buffer[256]; int len; while(e) { len = snprintf(buffer, sizeof(buffer), "struct ctlelem: socket=%d\n", e->socket); write(fd, buffer, len); e = e->entries.le_next; }}static voidwrite_option_list(int fd){ char buffer[256]; int len; int i; 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); }}#endif/* Handler for the SIGTERM signal (kill) */static voidsigterm(int sig){ /*int save_errno = errno;*/ signal(sig, SIG_IGN); syslog(LOG_NOTICE, "received signal %d, good-bye", sig); quitting = 1; /*errno = save_errno;*/}/* record the startup time, for returning uptime */static voidset_startup_time(int sysuptime){ startup_time = time(NULL); if(sysuptime) { /* use system uptime instead of daemon uptime */#if defined(__linux__) char buff[64]; int uptime, fd; fd = open("/proc/uptime", O_RDONLY); if(fd < 0) { syslog(LOG_ERR, "open(\"/proc/uptime\" : %m"); } else { memset(buff, 0, sizeof(buff)); read(fd, buff, sizeof(buff) - 1); uptime = atoi(buff); syslog(LOG_INFO, "system uptime is %d seconds", uptime); close(fd); 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 */ int n_lan_addr; /*const char * lan_addr[MAX_LAN_ADDR];*/ struct lan_addr_s lan_addr[MAX_LAN_ADDR]; int port; /* HTTP Port */ 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 * ex: 192.168.1.1/24 * return value : * 0 : ok * -1 : error */static intparselanaddr(struct lan_addr_s * lan_addr, const char * str){ const char * p; int nbits = 24; int n; p = str; while(*p && *p != '/') p++; if(*p == '/') nbits = atoi(p+1); n = p - str; if(n>15) { fprintf(stderr, "Error parsing address/mask : %s\n", str); return -1; } memcpy(lan_addr->str, str, n); lan_addr->str[n] = '\0'; if(!inet_aton(lan_addr->str, &lan_addr->addr)) { fprintf(stderr, "Error parsing address/mask : %s\n", str); return -1; } lan_addr->mask.s_addr = htonl(nbits ? (0xffffffff << (32 - nbits)) : 0); 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 intinit(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; const char * optionsfile = "/etc/miniupnpd.conf"; /* 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; } } /* set initial values */ v->n_lan_addr = 0; 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 */ /* 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<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: if(v->n_lan_addr < MAX_LAN_ADDR) { /*v->lan_addr[v->n_lan_addr++] = ary_options[i].value;*/ if(parselanaddr(&v->lan_addr[v->n_lan_addr], ary_options[i].value) == 0) v->n_lan_addr++; } else { fprintf(stderr, "Too many listening ips (max: %d), ignoring %s\n", MAX_LAN_ADDR, ary_options[i].value); } 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 UPNPNOTIFY_INTERVAL: v->notify_interval = atoi(ary_options[i].value); break; case UPNPSYSTEM_UPTIME: if(strcmp(ary_options[i].value, "yes") == 0) sysuptime = 1; break; case UPNPPACKET_LOG: if(strcmp(ary_options[i].value, "yes") == 0) logpackets = 1; break; 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; default: fprintf(stderr, "Unknown option in file %s\n", optionsfile); } } } /* 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 '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 '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; case 'U': sysuptime = 1; break; /*case 'l': logfilename = argv[++i]; break;*/ case 'L': logpackets = 1; 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; case 'p': if(i+1 < argc)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -