/* process HTTP or SSDP requests */ int main(int argc, char **argv) { int ret, i; int shttpl = -1; int smonitor = -1; LIST_HEAD(httplisthead, upnphttp) upnphttphead; struct upnphttp * e = 0; struct upnphttp * next; fd_set readset; /* for select() */ fd_set writeset; struct timeval timeout, timeofday, lastnotifytime = {0, 0}; time_t lastupdatetime = 0; int max_fd = -1; int last_changecnt = 0; pid_t scanner_pid = 0; pthread_t inotify_thread = 0; #ifdef TIVO_SUPPORT uint8_t beacon_interval = 5; int sbeacon = -1; struct sockaddr_in tivo_bcast; struct timeval lastbeacontime = {0, 0}; #endif for (i = 0; i < L_MAX; i++) log_level[i] = E_WARN; init_nls(); ret = init(argc, argv); if (ret != 0) return 1; #if (!defined(RTN66U) && !defined(RTN56U)) init_icon(PATH_ICON_PNG_SM); init_icon(PATH_ICON_PNG_LRG); init_icon(PATH_ICON_JPEG_SM); init_icon(PATH_ICON_JPEG_LRG); #endif DPRINTF(E_WARN, L_GENERAL, "Starting " SERVER_NAME " version " MINIDLNA_VERSION ".\n"); if (sqlite3_libversion_number() < 3005001) { DPRINTF(E_WARN, L_GENERAL, "SQLite library is old. Please use version 3.5.1 or newer.\n"); } LIST_INIT(&upnphttphead); ret = open_db(NULL); if (ret == 0) { updateID = sql_get_int_field(db, "SELECT VALUE from SETTINGS where KEY = 'UPDATE_ID'"); if (updateID == -1) ret = -1; } check_db(db, ret, &scanner_pid); #ifdef HAVE_INOTIFY if( GETFLAG(INOTIFY_MASK) ) { if (!sqlite3_threadsafe() || sqlite3_libversion_number() < 3005001) DPRINTF(E_ERROR, L_GENERAL, "SQLite library is not threadsafe! " "Inotify will be disabled.\n"); else if (pthread_create(&inotify_thread, NULL, start_inotify, NULL) != 0) DPRINTF(E_FATAL, L_GENERAL, "ERROR: pthread_create() failed for start_inotify. EXITING\n"); } #endif smonitor = OpenAndConfMonitorSocket(); sssdp = OpenAndConfSSDPReceiveSocket(); if (sssdp < 0) { DPRINTF(E_INFO, L_GENERAL, "Failed to open socket for receiving SSDP. Trying to use MiniSSDPd\n"); if (SubmitServicesToMiniSSDPD(lan_addr[0].str, runtime_vars.port) < 0) DPRINTF(E_FATAL, L_GENERAL, "Failed to connect to MiniSSDPd. EXITING"); } /* open socket for HTTP connections. */ shttpl = OpenAndConfHTTPSocket(runtime_vars.port); if (shttpl < 0) DPRINTF(E_FATAL, L_GENERAL, "Failed to open socket for HTTP. EXITING\n"); DPRINTF(E_WARN, L_GENERAL, "HTTP listening on port %d\n", runtime_vars.port); #ifdef TIVO_SUPPORT if (GETFLAG(TIVO_MASK)) { DPRINTF(E_WARN, L_GENERAL, "TiVo support is enabled.\n"); /* Add TiVo-specific randomize function to sqlite */ ret = sqlite3_create_function(db, "tivorandom", 1, SQLITE_UTF8, NULL, &TiVoRandomSeedFunc, NULL, NULL); if (ret != SQLITE_OK) DPRINTF(E_ERROR, L_TIVO, "ERROR: Failed to add sqlite randomize function for TiVo!\n"); /* open socket for sending Tivo notifications */ sbeacon = OpenAndConfTivoBeaconSocket(); if(sbeacon < 0) DPRINTF(E_FATAL, L_GENERAL, "Failed to open sockets for sending Tivo beacon notify " "messages. EXITING\n"); tivo_bcast.sin_family = AF_INET; tivo_bcast.sin_addr.s_addr = htonl(getBcastAddress()); tivo_bcast.sin_port = htons(2190); } #endif reload_ifaces(0); lastnotifytime.tv_sec = time(NULL) + runtime_vars.notify_interval; /* main loop */ while (!quitting) { /* Check if we need to send SSDP NOTIFY messages and do it if * needed */ if (gettimeofday(&timeofday, 0) < 0) { DPRINTF(E_ERROR, L_GENERAL, "gettimeofday(): %s\n", strerror(errno)); timeout.tv_sec = runtime_vars.notify_interval; timeout.tv_usec = 0; } else { /* the comparison is not very precise but who cares ? */ if (timeofday.tv_sec >= (lastnotifytime.tv_sec + runtime_vars.notify_interval)) { DPRINTF(E_DEBUG, L_SSDP, "Sending SSDP notifies\n"); for (i = 0; i < n_lan_addr; i++) { SendSSDPNotifies(lan_addr[i].snotify, lan_addr[i].str, runtime_vars.port, runtime_vars.notify_interval); } memcpy(&lastnotifytime, &timeofday, sizeof(struct timeval)); timeout.tv_sec = runtime_vars.notify_interval; timeout.tv_usec = 0; } else { timeout.tv_sec = lastnotifytime.tv_sec + runtime_vars.notify_interval - timeofday.tv_sec; if (timeofday.tv_usec > lastnotifytime.tv_usec) { timeout.tv_usec = 1000000 + lastnotifytime.tv_usec - timeofday.tv_usec; timeout.tv_sec--; } else timeout.tv_usec = lastnotifytime.tv_usec - timeofday.tv_usec; } #ifdef TIVO_SUPPORT if (sbeacon >= 0) { if (timeofday.tv_sec >= (lastbeacontime.tv_sec + beacon_interval)) { sendBeaconMessage(sbeacon, &tivo_bcast, sizeof(struct sockaddr_in), 1); memcpy(&lastbeacontime, &timeofday, sizeof(struct timeval)); if (timeout.tv_sec > beacon_interval) { timeout.tv_sec = beacon_interval; timeout.tv_usec = 0; } /* Beacons should be sent every 5 seconds or so for the first minute, * then every minute or so thereafter. */ if (beacon_interval == 5 && (timeofday.tv_sec - startup_time) > 60) beacon_interval = 60; } else if (timeout.tv_sec > (lastbeacontime.tv_sec + beacon_interval + 1 - timeofday.tv_sec)) timeout.tv_sec = lastbeacontime.tv_sec + beacon_interval - timeofday.tv_sec; } #endif } if (scanning) { if (!scanner_pid || kill(scanner_pid, 0) != 0) { scanning = 0; updateID++; } } /* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */ FD_ZERO(&readset); if (sssdp >= 0) { FD_SET(sssdp, &readset); max_fd = MAX(max_fd, sssdp); } if (shttpl >= 0) { FD_SET(shttpl, &readset); max_fd = MAX(max_fd, shttpl); } #ifdef TIVO_SUPPORT if (sbeacon >= 0) { FD_SET(sbeacon, &readset); max_fd = MAX(max_fd, sbeacon); } #endif if (smonitor >= 0) { FD_SET(smonitor, &readset); max_fd = MAX(max_fd, smonitor); } 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)) { FD_SET(e->socket, &readset); max_fd = MAX(max_fd, e->socket); i++; } } FD_ZERO(&writeset); upnpevents_selectfds(&readset, &writeset, &max_fd); ret = select(max_fd+1, &readset, &writeset, 0, &timeout); if (ret < 0) { if(quitting) goto shutdown; if(errno == EINTR) continue; DPRINTF(E_ERROR, L_GENERAL, "select(all): %s\n", strerror(errno)); DPRINTF(E_FATAL, L_GENERAL, "Failed to select open sockets. EXITING\n"); } upnpevents_processfds(&readset, &writeset); /* process SSDP packets */ if (sssdp >= 0 && FD_ISSET(sssdp, &readset)) { /*DPRINTF(E_DEBUG, L_GENERAL, "Received SSDP Packet\n");*/ ProcessSSDPRequest(sssdp, (unsigned short)runtime_vars.port); } #ifdef TIVO_SUPPORT if (sbeacon >= 0 && FD_ISSET(sbeacon, &readset)) { /*DPRINTF(E_DEBUG, L_GENERAL, "Received UDP Packet\n");*/ ProcessTiVoBeacon(sbeacon); } #endif if (smonitor >= 0 && FD_ISSET(smonitor, &readset)) { ProcessMonitorEvent(smonitor); } /* increment SystemUpdateID if the content database has changed, * and if there is an active HTTP connection, at most once every 2 seconds */ if (i && (timeofday.tv_sec >= (lastupdatetime + 2))) { if (scanning || sqlite3_total_changes(db) != last_changecnt) { updateID++; last_changecnt = sqlite3_total_changes(db); upnp_event_var_change_notify(EContentDirectory); lastupdatetime = timeofday.tv_sec; } } /* process active HTTP connections */ for (e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next) { if ((e->socket >= 0) && (e->state <= 2) && (FD_ISSET(e->socket, &readset))) Process_upnphttp(e); } /* process incoming HTTP connections */ if (shttpl >= 0 && FD_ISSET(shttpl, &readset)) { int shttp; socklen_t clientnamelen; struct sockaddr_in clientname; clientnamelen = sizeof(struct sockaddr_in); shttp = accept(shttpl, (struct sockaddr *)&clientname, &clientnamelen); if (shttp<0) { DPRINTF(E_ERROR, L_GENERAL, "accept(http): %s\n", strerror(errno)); } else { struct upnphttp * tmp = 0; DPRINTF(E_DEBUG, L_GENERAL, "HTTP connection from %s:%d\n", inet_ntoa(clientname.sin_addr), ntohs(clientname.sin_port) ); /*if (fcntl(shttp, F_SETFL, O_NONBLOCK) < 0) { DPRINTF(E_ERROR, L_GENERAL, "fcntl F_SETFL, O_NONBLOCK\n"); }*/ /* 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 { DPRINTF(E_ERROR, L_GENERAL, "New_upnphttp() failed\n"); close(shttp); } } } /* delete finished HTTP connections */ for (e = upnphttphead.lh_first; e != NULL; e = next) { next = e->entries.le_next; if(e->state >= 100) { LIST_REMOVE(e, entries); Delete_upnphttp(e); } } } shutdown: /* kill the scanner */ if (scanning && scanner_pid) kill(scanner_pid, SIGKILL); /* kill other child processes */ process_reap_children(); free(children); /* close out open sockets */ while (upnphttphead.lh_first != NULL) { e = upnphttphead.lh_first; LIST_REMOVE(e, entries); Delete_upnphttp(e); } if (sssdp >= 0) close(sssdp); if (shttpl >= 0) close(shttpl); #ifdef TIVO_SUPPORT if (sbeacon >= 0) close(sbeacon); #endif for (i = 0; i < n_lan_addr; i++) { SendSSDPGoodbyes(lan_addr[i].snotify); close(lan_addr[i].snotify); } if (inotify_thread) pthread_join(inotify_thread, NULL); sql_exec(db, "UPDATE SETTINGS set VALUE = '%u' where KEY = 'UPDATE_ID'", updateID); sqlite3_close(db); upnpevents_removeSubscribers(); if (pidfilename && unlink(pidfilename) < 0) DPRINTF(E_ERROR, L_GENERAL, "Failed to remove pidfile %s: %s\n", pidfilename, strerror(errno)); log_close(); freeoptions(); exit(EXIT_SUCCESS); }
/* init phase : * 1) read configuration file * 2) read command line arguments * 3) daemonize * 4) check and write pid file * 5) set startup time stamp * 6) compute presentation URL * 7) set signal handlers */ static int init(int argc, char **argv) { int i; int pid; int debug_flag = 0; int verbose_flag = 0; int options_flag = 0; struct sigaction sa; const char * presurl = NULL; const char * optionsfile = "/etc/minidlna.conf"; char mac_str[13]; char *string, *word; char *path; char buf[PATH_MAX]; char log_str[75] = "general,artwork,database,inotify,scanner,metadata,http,ssdp,tivo=warn"; char *log_level = NULL; struct media_dir_s *media_dir; int ifaces = 0; media_types types; uid_t uid = 0; /* first check if "-f" option is used */ for (i=2; i<argc; i++) { if (strcmp(argv[i-1], "-f") == 0) { optionsfile = argv[i]; options_flag = 1; break; } } /* set up uuid based on mac address */ if (getsyshwaddr(mac_str, sizeof(mac_str)) < 0) { DPRINTF(E_OFF, L_GENERAL, "No MAC address found. Falling back to generic UUID.\n"); strcpy(mac_str, "554e4b4e4f57"); } strcpy(uuidvalue+5, "4d696e69-444c-164e-9d41-"); strncat(uuidvalue, mac_str, 12); getfriendlyname(friendly_name, FRIENDLYNAME_MAX_LEN); runtime_vars.port = 8200; runtime_vars.notify_interval = 895; /* seconds between SSDP announces */ runtime_vars.max_connections = 50; runtime_vars.root_container = NULL; runtime_vars.ifaces[0] = NULL; /* 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) DPRINTF(E_FATAL, L_GENERAL, "Error reading configuration file %s\n", optionsfile); } for (i=0; i<num_options; i++) { switch (ary_options[i].id) { case UPNPIFNAME: for (string = ary_options[i].value; (word = strtok(string, ",")); string = NULL) { if (ifaces >= MAX_LAN_ADDR) { DPRINTF(E_ERROR, L_GENERAL, "Too many interfaces (max: %d), ignoring %s\n", MAX_LAN_ADDR, word); break; } runtime_vars.ifaces[ifaces++] = word; } break; case UPNPPORT: runtime_vars.port = atoi(ary_options[i].value); break; case UPNPPRESENTATIONURL: presurl = ary_options[i].value; break; case UPNPNOTIFY_INTERVAL: runtime_vars.notify_interval = atoi(ary_options[i].value); break; case UPNPSERIAL: strncpyt(serialnumber, ary_options[i].value, SERIALNUMBER_MAX_LEN); break; case UPNPMODEL_NAME: strncpyt(modelname, ary_options[i].value, MODELNAME_MAX_LEN); break; case UPNPMODEL_NUMBER: strncpyt(modelnumber, ary_options[i].value, MODELNUMBER_MAX_LEN); break; case UPNPFRIENDLYNAME: strncpyt(friendly_name, ary_options[i].value, FRIENDLYNAME_MAX_LEN); break; case UPNPMEDIADIR: types = ALL_MEDIA; path = ary_options[i].value; word = strchr(path, ','); if (word && (access(path, F_OK) != 0)) { types = 0; while (*path) { if (*path == ',') { path++; break; } else if (*path == 'A' || *path == 'a') types |= TYPE_AUDIO; else if (*path == 'V' || *path == 'v') types |= TYPE_VIDEO; else if (*path == 'P' || *path == 'p') types |= TYPE_IMAGES; else DPRINTF(E_FATAL, L_GENERAL, "Media directory entry not understood [%s]\n", ary_options[i].value); path++; } } path = realpath(path, buf); if (!path || access(path, F_OK) != 0) { DPRINTF(E_ERROR, L_GENERAL, "Media directory \"%s\" not accessible [%s]\n", ary_options[i].value, strerror(errno)); break; } media_dir = calloc(1, sizeof(struct media_dir_s)); media_dir->path = strdup(path); media_dir->types = types; if (media_dirs) { struct media_dir_s *all_dirs = media_dirs; while( all_dirs->next ) all_dirs = all_dirs->next; all_dirs->next = media_dir; } else media_dirs = media_dir; break; case UPNPALBUMART_NAMES: for (string = ary_options[i].value; (word = strtok(string, "/")); string = NULL) { struct album_art_name_s * this_name = calloc(1, sizeof(struct album_art_name_s)); int len = strlen(word); if (word[len-1] == '*') { word[len-1] = '\0'; this_name->wildcard = 1; } this_name->name = strdup(word); if (album_art_names) { struct album_art_name_s * all_names = album_art_names; while( all_names->next ) all_names = all_names->next; all_names->next = this_name; } else album_art_names = this_name; } break; case UPNPDBDIR: path = realpath(ary_options[i].value, buf); if (!path) path = (ary_options[i].value); make_dir(path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); if (access(path, F_OK) != 0) DPRINTF(E_FATAL, L_GENERAL, "Database path not accessible! [%s]\n", path); strncpyt(db_path, path, PATH_MAX); break; case UPNPLOGDIR: path = realpath(ary_options[i].value, buf); if (!path) path = (ary_options[i].value); make_dir(path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); if (access(path, F_OK) != 0) DPRINTF(E_FATAL, L_GENERAL, "Log path not accessible! [%s]\n", path); strncpyt(log_path, path, PATH_MAX); break; case UPNPLOGLEVEL: log_level = ary_options[i].value; break; case UPNPINOTIFY: if (!strtobool(ary_options[i].value)) CLEARFLAG(INOTIFY_MASK); break; case ENABLE_TIVO: if (strtobool(ary_options[i].value)) SETFLAG(TIVO_MASK); break; case ENABLE_DLNA_STRICT: if (strtobool(ary_options[i].value)) SETFLAG(DLNA_STRICT_MASK); break; case ROOT_CONTAINER: switch (ary_options[i].value[0]) { case '.': runtime_vars.root_container = NULL; break; case 'B': case 'b': runtime_vars.root_container = BROWSEDIR_ID; break; case 'M': case 'm': runtime_vars.root_container = MUSIC_ID; break; case 'V': case 'v': runtime_vars.root_container = VIDEO_ID; break; case 'P': case 'p': runtime_vars.root_container = IMAGE_ID; break; default: runtime_vars.root_container = ary_options[i].value; DPRINTF(E_WARN, L_GENERAL, "Using arbitrary root container [%s]\n", ary_options[i].value); break; } break; case UPNPMINISSDPDSOCKET: minissdpdsocketpath = ary_options[i].value; break; case UPNPUUID: strcpy(uuidvalue+5, ary_options[i].value); break; case USER_ACCOUNT: uid = strtoul(ary_options[i].value, &string, 0); if (*string) { /* Symbolic username given, not UID. */ struct passwd *entry = getpwnam(ary_options[i].value); if (!entry) DPRINTF(E_FATAL, L_GENERAL, "Bad user '%s'.\n", argv[i]); uid = entry->pw_uid; } break; case FORCE_SORT_CRITERIA: force_sort_criteria = ary_options[i].value; break; case MAX_CONNECTIONS: runtime_vars.max_connections = atoi(ary_options[i].value); break; case MERGE_MEDIA_DIRS: if (strtobool(ary_options[i].value)) SETFLAG(MERGE_MEDIA_DIRS_MASK); break; default: DPRINTF(E_ERROR, L_GENERAL, "Unknown option in file %s\n", optionsfile); } } if (log_path[0] == '\0') { if (db_path[0] == '\0') strncpyt(log_path, DEFAULT_LOG_PATH, PATH_MAX); else strncpyt(log_path, db_path, PATH_MAX); } if (db_path[0] == '\0') strncpyt(db_path, DEFAULT_DB_PATH, PATH_MAX); /* command line arguments processing */ for (i=1; i<argc; i++) { if (argv[i][0] != '-') { DPRINTF(E_FATAL, L_GENERAL, "Unknown option: %s\n", argv[i]); } else if (strcmp(argv[i], "--help") == 0) { runtime_vars.port = -1; break; } else switch(argv[i][1]) { case 't': if (i+1 < argc) runtime_vars.notify_interval = atoi(argv[++i]); else DPRINTF(E_FATAL, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); break; case 's': if (i+1 < argc) strncpyt(serialnumber, argv[++i], SERIALNUMBER_MAX_LEN); else DPRINTF(E_FATAL, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); break; case 'm': if (i+1 < argc) strncpyt(modelnumber, argv[++i], MODELNUMBER_MAX_LEN); else DPRINTF(E_FATAL, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); break; case 'p': if (i+1 < argc) runtime_vars.port = atoi(argv[++i]); else DPRINTF(E_FATAL, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); break; case 'P': if (i+1 < argc) { if (argv[++i][0] != '/') DPRINTF(E_FATAL, L_GENERAL, "Option -%c requires an absolute filename.\n", argv[i-1][1]); else pidfilename = argv[i]; } else DPRINTF(E_FATAL, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); break; case 'd': debug_flag = 1; case 'v': verbose_flag = 1; break; case 'L': SETFLAG(NO_PLAYLIST_MASK); break; case 'w': if (i+1 < argc) presurl = argv[++i]; else DPRINTF(E_FATAL, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); break; case 'i': if (i+1 < argc) { i++; if (ifaces >= MAX_LAN_ADDR) { DPRINTF(E_ERROR, L_GENERAL, "Too many interfaces (max: %d), ignoring %s\n", MAX_LAN_ADDR, argv[i]); break; } runtime_vars.ifaces[ifaces++] = argv[i]; } else DPRINTF(E_FATAL, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); break; case 'f': i++; /* discarding, the config file is already read */ break; case 'h': runtime_vars.port = -1; // triggers help display break; case 'R': snprintf(buf, sizeof(buf), "rm -rf %s/files.db %s/art_cache", db_path, db_path); if (system(buf) != 0) DPRINTF(E_FATAL, L_GENERAL, "Failed to clean old file cache. EXITING\n"); break; case 'u': if (i+1 != argc) { i++; uid = strtoul(argv[i], &string, 0); if (*string) { /* Symbolic username given, not UID. */ struct passwd *entry = getpwnam(argv[i]); if (!entry) DPRINTF(E_FATAL, L_GENERAL, "Bad user '%s'.\n", argv[i]); uid = entry->pw_uid; } } else DPRINTF(E_FATAL, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); break; break; #ifdef __linux__ case 'S': SETFLAG(SYSTEMD_MASK); break; #endif case 'V': printf("Version " MINIDLNA_VERSION "\n"); exit(0); break; default: DPRINTF(E_ERROR, L_GENERAL, "Unknown option: %s\n", argv[i]); runtime_vars.port = -1; // triggers help display } } if (runtime_vars.port <= 0) { printf("Usage:\n\t" "%s [-d] [-v] [-f config_file] [-p port]\n" "\t\t[-i network_interface] [-u uid_to_run_as]\n" "\t\t[-t notify_interval] [-P pid_filename]\n" "\t\t[-s serial] [-m model_number]\n" #ifdef __linux__ "\t\t[-w url] [-R] [-L] [-S] [-V] [-h]\n" #else "\t\t[-w url] [-R] [-L] [-V] [-h]\n" #endif "\nNotes:\n\tNotify interval is in seconds. Default is 895 seconds.\n" "\tDefault pid file is %s.\n" "\tWith -d minidlna will run in debug mode (not daemonize).\n" "\t-w sets the presentation url. Default is http address on port 80\n" "\t-v enables verbose output\n" "\t-h displays this text\n" "\t-R forces a full rescan\n" "\t-L do not create playlists\n" #ifdef __linux__ "\t-S changes behaviour for systemd\n" #endif "\t-V print the version number\n", argv[0], pidfilename); return 1; } if (verbose_flag) { strcpy(log_str+65, "debug"); log_level = log_str; } else if (!log_level) log_level = log_str; /* Set the default log file path to NULL (stdout) */ path = NULL; if (debug_flag) { pid = getpid(); strcpy(log_str+65, "maxdebug"); log_level = log_str; } else if (GETFLAG(SYSTEMD_MASK)) { pid = getpid(); } else { pid = process_daemonize(); #ifdef READYNAS unlink("/ramfs/.upnp-av_scan"); path = "/var/log/upnp-av.log"; #else if (access(db_path, F_OK) != 0) make_dir(db_path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); snprintf(buf, sizeof(buf), "%s/minidlna.log", log_path); path = buf; #endif } log_init(path, log_level); if (process_check_if_running(pidfilename) < 0) { DPRINTF(E_ERROR, L_GENERAL, SERVER_NAME " is already running. EXITING.\n"); return 1; } set_startup_time(); /* presentation url */ if (presurl) strncpyt(presentationurl, presurl, PRESENTATIONURL_MAX_LEN); else strcpy(presentationurl, "/"); /* set signal handlers */ memset(&sa, 0, sizeof(struct sigaction)); sa.sa_handler = sigterm; if (sigaction(SIGTERM, &sa, NULL)) DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n", "SIGTERM"); if (sigaction(SIGINT, &sa, NULL)) DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n", "SIGINT"); if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n", "SIGPIPE"); if (signal(SIGHUP, &sighup) == SIG_ERR) DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n", "SIGHUP"); signal(SIGUSR1, &sigusr1); sa.sa_handler = process_handle_child_termination; if (sigaction(SIGCHLD, &sa, NULL)) DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n", "SIGCHLD"); if (writepidfile(pidfilename, pid, uid) != 0) pidfilename = NULL; if (uid > 0) { struct stat st; if (stat(db_path, &st) == 0 && st.st_uid != uid && chown(db_path, uid, -1) != 0) DPRINTF(E_ERROR, L_GENERAL, "Unable to set db_path [%s] ownership to %d: %s\n", db_path, uid, strerror(errno)); } if (uid > 0 && setuid(uid) == -1) DPRINTF(E_FATAL, L_GENERAL, "Failed to switch to uid '%d'. [%s] EXITING.\n", uid, strerror(errno)); children = calloc(runtime_vars.max_connections, sizeof(struct child)); if (!children) { DPRINTF(E_ERROR, L_GENERAL, "Allocation failed\n"); return 1; } // remove working flag remove_scantag(); return 0; }
/* ProcessSSDPRequest() * process SSDP M-SEARCH requests and responds to them */ void ProcessSSDPRequest(int s, unsigned short port) { int n; char bufr[1500]; socklen_t len_r; struct sockaddr_in sendername; int i; char *st = NULL, *mx = NULL, *man = NULL, *mx_end = NULL; int man_len = 0; len_r = sizeof(struct sockaddr_in); n = recvfrom(s, bufr, sizeof(bufr)-1, 0, (struct sockaddr *)&sendername, &len_r); if (n < 0) { DPRINTF(E_ERROR, L_SSDP, "recvfrom(udp): %s\n", strerror(errno)); return; } bufr[n] = '\0'; n -= 2; if (memcmp(bufr, "NOTIFY", 6) == 0) { char *loc = NULL, *srv = NULL, *nts = NULL, *nt = NULL; int loc_len = 0; //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Received SSDP notify:\n%.*s", n, bufr); for (i = 0; i < n; i++) { if( bufr[i] == '*' ) break; } if (strcasestrc(bufr+i, "HTTP/1.1", '\r') == NULL) return; while (i < n) { while ((i < n) && (bufr[i] != '\r' || bufr[i+1] != '\n')) i++; i += 2; if (strncasecmp(bufr+i, "SERVER:", 7) == 0) { srv = bufr+i+7; while (*srv == ' ' || *srv == '\t') srv++; } else if (strncasecmp(bufr+i, "LOCATION:", 9) == 0) { loc = bufr+i+9; while (*loc == ' ' || *loc == '\t') loc++; while (loc[loc_len]!='\r' && loc[loc_len]!='\n') loc_len++; } else if (strncasecmp(bufr+i, "NTS:", 4) == 0) { nts = bufr+i+4; while (*nts == ' ' || *nts == '\t') nts++; } else if (strncasecmp(bufr+i, "NT:", 3) == 0) { nt = bufr+i+3; while(*nt == ' ' || *nt == '\t') nt++; } } if (!loc || !srv || !nt || !nts || (strncmp(nts, "ssdp:alive", 10) != 0) || (strncmp(nt, "urn:schemas-upnp-org:device:MediaRenderer", 41) != 0)) return; loc[loc_len] = '\0'; if ((strncmp(srv, "Allegro-Software-RomPlug", 24) == 0) || /* Roku */ (strstr(loc, "SamsungMRDesc.xml") != NULL) || /* Samsung TV */ (strstrc(srv, "DigiOn DiXiM", '\r') != NULL)) /* Marantz Receiver */ { /* Check if the client is already in cache */ i = SearchClientCache(sendername.sin_addr, 1); if (i >= 0) { if (clients[i].type < EStandardDLNA150 && clients[i].type != ESamsungSeriesA) { clients[i].age = time(NULL); return; } } ParseUPnPClient(loc); } } else if (memcmp(bufr, "M-SEARCH", 8) == 0) { int st_len = 0, mx_len = 0, mx_val = 0; //DPRINTF(E_DEBUG, L_SSDP, "Received SSDP request:\n%.*s\n", n, bufr); for (i = 0; i < n; i++) { if (bufr[i] == '*') break; } if (strcasestrc(bufr+i, "HTTP/1.1", '\r') == NULL) return; while (i < n) { while ((i < n) && (bufr[i] != '\r' || bufr[i+1] != '\n')) i++; i += 2; if (strncasecmp(bufr+i, "ST:", 3) == 0) { st = bufr+i+3; st_len = 0; while (*st == ' ' || *st == '\t') st++; while (st[st_len]!='\r' && st[st_len]!='\n') st_len++; } else if (strncasecmp(bufr+i, "MX:", 3) == 0) { mx = bufr+i+3; mx_len = 0; while (*mx == ' ' || *mx == '\t') mx++; while (mx[mx_len]!='\r' && mx[mx_len]!='\n') mx_len++; mx_val = strtol(mx, &mx_end, 10); } else if (strncasecmp(bufr+i, "MAN:", 4) == 0) { man = bufr+i+4; man_len = 0; while (*man == ' ' || *man == '\t') man++; while (man[man_len]!='\r' && man[man_len]!='\n') man_len++; } } /*DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH packet received from %s:%d\n", inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port) );*/ if (GETFLAG(DLNA_STRICT_MASK) && (ntohs(sendername.sin_port) <= 1024 || ntohs(sendername.sin_port) == 1900)) { DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad source port %d]\n", inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port)); } else if (!man || (strncmp(man, "\"ssdp:discover\"", 15) != 0)) { DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad %s header '%.*s']\n", inet_ntoa(sendername.sin_addr), "MAN", man_len, man); } else if (!mx || mx == mx_end || mx_val < 0) { DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad %s header '%.*s']\n", inet_ntoa(sendername.sin_addr), "MX", mx_len, mx); } else if (st && (st_len > 0)) { int l; int lan_addr_index = 0; /* find in which sub network the client is */ for (i = 0; i < n_lan_addr; i++) { if((sendername.sin_addr.s_addr & lan_addr[i].mask.s_addr) == (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr)) { lan_addr_index = i; break; } } if (n_lan_addr == i) { DPRINTF(E_DEBUG, L_SSDP, "Ignoring SSDP M-SEARCH on other interface [%s]\n", inet_ntoa(sendername.sin_addr)); return; } DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH from %s:%d ST: %.*s, MX: %.*s, MAN: %.*s\n", inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port), st_len, st, mx_len, mx, man_len, man); /* Responds to request with a device as ST header */ for (i = 0; known_service_types[i]; i++) { l = strlen(known_service_types[i]); if ((l <= st_len) && (memcmp(st, known_service_types[i], l) == 0)) { if (st_len != l) { /* Check version number - must always be 1 currently. */ if ((st[l-1] == ':') && (st[l] == '1')) l++; while (l < st_len) { if (!isspace(st[l])) { DPRINTF(E_DEBUG, L_SSDP, "Ignoring SSDP M-SEARCH with bad extra data [%s]\n", inet_ntoa(sendername.sin_addr)); break; } l++; } if (l != st_len) break; } _usleep(random()>>20); SendSSDPAnnounce2(s, sendername, i, lan_addr[lan_addr_index].str, port); break; } } /* Responds to request with ST: ssdp:all */ /* strlen("ssdp:all") == 8 */ if ((st_len == 8) && (memcmp(st, "ssdp:all", 8) == 0)) { for (i=0; known_service_types[i]; i++) { l = strlen(known_service_types[i]); SendSSDPAnnounce2(s, sendername, i, lan_addr[lan_addr_index].str, port); } } }
/* add_redirect_rule2() : * create a rdr rule */ int add_redirect_rule2(const char * ifname, const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, int proto, const char * desc, unsigned int timestamp) { int r; struct pfioc_rule pcr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; struct pf_pooladdr *a; #endif if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } r = 0; memset(&pcr, 0, sizeof(pcr)); strlcpy(pcr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m"); r = -1; } else { pcr.pool_ticket = pp.ticket; #else if(1) { pcr.rule.direction = PF_IN; //pcr.rule.src.addr.type = PF_ADDR_NONE; pcr.rule.src.addr.type = PF_ADDR_ADDRMASK; pcr.rule.dst.addr.type = PF_ADDR_ADDRMASK; pcr.rule.nat.addr.type = PF_ADDR_NONE; pcr.rule.rdr.addr.type = PF_ADDR_ADDRMASK; #endif pcr.rule.dst.port_op = PF_OP_EQ; pcr.rule.dst.port[0] = htons(eport); pcr.rule.dst.port[1] = htons(eport); #ifndef PF_NEWSTYLE pcr.rule.action = PF_RDR; #ifndef PF_ENABLE_FILTER_RULES pcr.rule.natpass = 1; #else pcr.rule.natpass = 0; #endif #else #ifndef PF_ENABLE_FILTER_RULES pcr.rule.action = PF_PASS; #else pcr.rule.action = PF_MATCH; #endif #endif pcr.rule.af = AF_INET; #ifdef USE_IFNAME_IN_RULES if(ifname) strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ); #endif pcr.rule.proto = proto; pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/ #ifdef PFRULE_HAS_RTABLEID pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */ #endif pcr.rule.quick = 1; pcr.rule.keep_state = PF_STATE_NORMAL; if(tag) strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE); strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE); if(rhost && rhost[0] != '\0' && rhost[0] != '*') { inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr); pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); } #ifndef PF_NEWSTYLE pcr.rule.rpool.proxy_port[0] = iport; pcr.rule.rpool.proxy_port[1] = iport; TAILQ_INIT(&pcr.rule.rpool.list); a = calloc(1, sizeof(struct pf_pooladdr)); inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr); a->addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); TAILQ_INSERT_TAIL(&pcr.rule.rpool.list, a, entries); memcpy(&pp.addr, a, sizeof(struct pf_pooladdr)); if(ioctl(dev, DIOCADDADDR, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCADDADDR, ...): %m"); r = -1; } else { #else pcr.rule.rdr.proxy_port[0] = iport; pcr.rule.rdr.proxy_port[1] = iport; inet_pton(AF_INET, iaddr, &pcr.rule.rdr.addr.v.a.addr.v4.s_addr); pcr.rule.rdr.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); if(1) { #endif pcr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); r = -1; } else { pcr.action = PF_CHANGE_ADD_TAIL; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m"); r = -1; } } } #ifndef PF_NEWSTYLE free(a); #endif } if(r == 0 && timestamp > 0) { struct timestamp_entry * tmp; tmp = malloc(sizeof(struct timestamp_entry)); if(tmp) { tmp->next = timestamp_list; tmp->timestamp = timestamp; tmp->eport = eport; tmp->protocol = (short)proto; timestamp_list = tmp; } } return r; } /* thanks to Seth Mos for this function */ int add_filter_rule2(const char * ifname, const char * rhost, const char * iaddr, unsigned short eport, unsigned short iport, int proto, const char * desc) { #ifndef PF_ENABLE_FILTER_RULES return 0; #else int r; struct pfioc_rule pcr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; struct pf_pooladdr *a; #endif if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } r = 0; memset(&pcr, 0, sizeof(pcr)); strlcpy(pcr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m"); r = -1; } else { pcr.pool_ticket = pp.ticket; #else if(1) { #endif pcr.rule.dst.port_op = PF_OP_EQ; pcr.rule.dst.port[0] = htons(eport); pcr.rule.direction = PF_IN; pcr.rule.action = PF_PASS; pcr.rule.af = AF_INET; #ifdef USE_IFNAME_IN_RULES if(ifname) strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ); #endif pcr.rule.proto = proto; pcr.rule.quick = (GETFLAG(PFNOQUICKRULESMASK))?0:1; pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/ /* see the discussion on the forum : * http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=638 */ pcr.rule.flags = TH_SYN; pcr.rule.flagset = (TH_SYN|TH_ACK); #ifdef PFRULE_HAS_RTABLEID pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */ #endif pcr.rule.keep_state = 1; strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE); if(queue) strlcpy(pcr.rule.qname, queue, PF_QNAME_SIZE); if(tag) strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE); if(rhost && rhost[0] != '\0' && rhost[0] != '*') { inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr); pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); } #ifndef PF_NEWSTYLE pcr.rule.rpool.proxy_port[0] = eport; a = calloc(1, sizeof(struct pf_pooladdr)); inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr); a->addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); memcpy(&pp.addr, a, sizeof(struct pf_pooladdr)); TAILQ_INIT(&pcr.rule.rpool.list); inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr); TAILQ_INSERT_TAIL(&pcr.rule.rpool.list, a, entries); /* we have any - any port = # keep state label */ /* we want any - iaddr port = # keep state label */ /* memcpy(&pcr.rule.dst, a, sizeof(struct pf_pooladdr)); */ memcpy(&pp.addr, a, sizeof(struct pf_pooladdr)); strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE); if(ioctl(dev, DIOCADDADDR, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCADDADDR, ...): %m"); r = -1; } else { #else if(1) { #endif pcr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); r = -1; } else { pcr.action = PF_CHANGE_ADD_TAIL; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m"); r = -1; } } } #ifndef PF_NEWSTYLE free(a); #endif } return r; #endif } /* get_redirect_rule() * return value : 0 success (found) * -1 = error or rule not found */ int get_redirect_rule(const char * ifname, unsigned short eport, int proto, char * iaddr, int iaddrlen, unsigned short * iport, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * timestamp, u_int64_t * packets, u_int64_t * bytes) { int i, n; struct pfioc_rule pr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; #endif if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE pr.rule.action = PF_RDR; #endif if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); goto error; } n = pr.nr; for(i=0; i<n; i++) { pr.nr = i; if(ioctl(dev, DIOCGETRULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); goto error; } if( (eport == ntohs(pr.rule.dst.port[0])) && (eport == ntohs(pr.rule.dst.port[1])) && (pr.rule.proto == proto) ) { #ifndef PF_NEWSTYLE *iport = pr.rule.rpool.proxy_port[0]; #else *iport = pr.rule.rdr.proxy_port[0]; #endif if(desc) strlcpy(desc, pr.rule.label, desclen); #ifdef PFRULE_INOUT_COUNTS if(packets) *packets = pr.rule.packets[0] + pr.rule.packets[1]; if(bytes) *bytes = pr.rule.bytes[0] + pr.rule.bytes[1]; #else if(packets) *packets = pr.rule.packets; if(bytes) *bytes = pr.rule.bytes; #endif #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); pp.r_action = PF_RDR; pp.r_num = i; pp.ticket = pr.ticket; if(ioctl(dev, DIOCGETADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m"); goto error; } if(pp.nr != 1) { syslog(LOG_NOTICE, "No address associated with pf rule"); goto error; } pp.nr = 0; /* first */ if(ioctl(dev, DIOCGETADDR, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m"); goto error; } inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr, iaddr, iaddrlen); #else inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr, iaddr, iaddrlen); #endif if(rhost && rhostlen > 0) { if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0) { rhost[0] = '\0'; /* empty string */ } else { inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr, rhost, rhostlen); } } if(timestamp) *timestamp = get_timestamp(eport, proto); return 0; } } error: return -1; } int delete_redirect_rule(const char * ifname, unsigned short eport, int proto) { int i, n; struct pfioc_rule pr; if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE pr.rule.action = PF_RDR; #endif if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); goto error; } n = pr.nr; for(i=0; i<n; i++) { pr.nr = i; if(ioctl(dev, DIOCGETRULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); goto error; } if( (eport == ntohs(pr.rule.dst.port[0])) && (eport == ntohs(pr.rule.dst.port[1])) && (pr.rule.proto == proto) ) { pr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); goto error; } pr.action = PF_CHANGE_REMOVE; pr.nr = i; if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m"); goto error; } remove_timestamp_entry(eport, proto); return 0; } } error: return -1; } int delete_filter_rule(const char * ifname, unsigned short eport, int proto) { #ifndef PF_ENABLE_FILTER_RULES return 0; #else int i, n; struct pfioc_rule pr; if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); pr.rule.action = PF_PASS; if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); goto error; } n = pr.nr; for(i=0; i<n; i++) { pr.nr = i; if(ioctl(dev, DIOCGETRULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); goto error; } if( (eport == ntohs(pr.rule.dst.port[0])) && (pr.rule.proto == proto) ) { pr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); goto error; } pr.action = PF_CHANGE_REMOVE; pr.nr = i; if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m"); goto error; } return 0; } } error: return -1; #endif } int get_redirect_rule_by_index(int index, char * ifname, unsigned short * eport, char * iaddr, int iaddrlen, unsigned short * iport, int * proto, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * timestamp, u_int64_t * packets, u_int64_t * bytes) { int n; struct pfioc_rule pr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; #endif if(index < 0) return -1; if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE pr.rule.action = PF_RDR; #endif if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); goto error; } n = pr.nr; if(index >= n) goto error; pr.nr = index; if(ioctl(dev, DIOCGETRULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); goto error; } *proto = pr.rule.proto; *eport = ntohs(pr.rule.dst.port[0]); #ifndef PF_NEWSTYLE *iport = pr.rule.rpool.proxy_port[0]; #else *iport = pr.rule.rdr.proxy_port[0]; #endif if(ifname) strlcpy(ifname, pr.rule.ifname, IFNAMSIZ); if(desc) strlcpy(desc, pr.rule.label, desclen); #ifdef PFRULE_INOUT_COUNTS if(packets) *packets = pr.rule.packets[0] + pr.rule.packets[1]; if(bytes) *bytes = pr.rule.bytes[0] + pr.rule.bytes[1]; #else if(packets) *packets = pr.rule.packets; if(bytes) *bytes = pr.rule.bytes; #endif #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); pp.r_action = PF_RDR; pp.r_num = index; pp.ticket = pr.ticket; if(ioctl(dev, DIOCGETADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m"); goto error; } if(pp.nr != 1) { syslog(LOG_NOTICE, "No address associated with pf rule"); goto error; } pp.nr = 0; /* first */ if(ioctl(dev, DIOCGETADDR, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m"); goto error; } inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr, iaddr, iaddrlen); #else inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr, iaddr, iaddrlen); #endif if(rhost && rhostlen > 0) { if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0) { rhost[0] = '\0'; /* empty string */ } else { inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr, rhost, rhostlen); } } if(timestamp) *timestamp = get_timestamp(*eport, *proto); return 0; error: return -1; } /* return an (malloc'ed) array of "external" port for which there is * a port mapping. number is the size of the array */ unsigned short * get_portmappings_in_range(unsigned short startport, unsigned short endport, int proto, unsigned int * number) { unsigned short * array; unsigned int capacity; int i, n; unsigned short eport; struct pfioc_rule pr; *number = 0; if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return NULL; } capacity = 128; array = calloc(capacity, sizeof(unsigned short)); if(!array) { syslog(LOG_ERR, "get_portmappings_in_range() : calloc error"); return NULL; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE pr.rule.action = PF_RDR; #endif if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); free(array); return NULL; } n = pr.nr; for(i=0; i<n; i++) { pr.nr = i; if(ioctl(dev, DIOCGETRULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); continue; } eport = ntohs(pr.rule.dst.port[0]); if( (eport == ntohs(pr.rule.dst.port[1])) && (pr.rule.proto == proto) && (startport <= eport) && (eport <= endport) ) { if(*number >= capacity) { /* need to increase the capacity of the array */ capacity += 128; array = realloc(array, sizeof(unsigned short)*capacity); if(!array) { syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity); *number = 0; return NULL; } } array[*number] = eport; (*number)++; } } return array; }
int64_t GetImageMetadata(const char *path, char *name) { ExifData *ed; ExifEntry *e = NULL; ExifLoader *l; struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; FILE *infile; int width=0, height=0, thumb=0; char make[32], model[64] = {'\0'}; char b[1024]; struct stat file; int64_t ret; image_s *imsrc; metadata_t m; uint32_t free_flags = 0xFFFFFFFF; memset(&m, '\0', sizeof(metadata_t)); //DEBUG DPRINTF(E_DEBUG, L_METADATA, "Parsing %s...\n", path); if ( stat(path, &file) != 0 ) return 0; strip_ext(name); //DEBUG DPRINTF(E_DEBUG, L_METADATA, " * size: %jd\n", file.st_size); /* MIME hard-coded to JPEG for now, until we add PNG support */ m.mime = strdup("image/jpeg"); l = exif_loader_new(); exif_loader_write_file(l, path); ed = exif_loader_get_data(l); exif_loader_unref(l); if( !ed ) goto no_exifdata; e = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF], EXIF_TAG_DATE_TIME_ORIGINAL); if( e || (e = exif_content_get_entry(ed->ifd[EXIF_IFD_EXIF], EXIF_TAG_DATE_TIME_DIGITIZED)) ) { m.date = strdup(exif_entry_get_value(e, b, sizeof(b))); if( strlen(m.date) > 10 ) { m.date[4] = '-'; m.date[7] = '-'; m.date[10] = 'T'; } else { free(m.date); m.date = NULL; } } else { /* One last effort to get the date from XMP */ image_get_jpeg_date_xmp(path, &m.date); } //DEBUG DPRINTF(E_DEBUG, L_METADATA, " * date: %s\n", m.date); e = exif_content_get_entry(ed->ifd[EXIF_IFD_0], EXIF_TAG_MAKE); if( e ) { strncpyt(make, exif_entry_get_value(e, b, sizeof(b)), sizeof(make)); e = exif_content_get_entry(ed->ifd[EXIF_IFD_0], EXIF_TAG_MODEL); if( e ) { strncpyt(model, exif_entry_get_value(e, b, sizeof(b)), sizeof(model)); if( !strcasestr(model, make) ) snprintf(model, sizeof(model), "%s %s", make, exif_entry_get_value(e, b, sizeof(b))); m.creator = escape_tag(trim(model), 1); } } //DEBUG DPRINTF(E_DEBUG, L_METADATA, " * model: %s\n", model); e = exif_content_get_entry(ed->ifd[EXIF_IFD_0], EXIF_TAG_ORIENTATION); if( e ) { switch( exif_get_short(e->data, exif_data_get_byte_order(ed)) ) { case 3: m.rotation = 180; break; case 6: m.rotation = 90; break; case 8: m.rotation = 270; break; default: m.rotation = 0; break; } } if( ed->size ) { /* We might need to verify that the thumbnail is 160x160 or smaller */ if( ed->size > 12000 ) { if( ed->size < 60000 ) { imsrc = image_new_from_jpeg(NULL, 0, ed->data, ed->size, 1, ROTATE_NONE); if( imsrc ) { if( (imsrc->width <= 160) && (imsrc->height <= 160) ) thumb = 1; image_free(imsrc); } } } else thumb = 1; } //DEBUG DPRINTF(E_DEBUG, L_METADATA, " * thumbnail: %d\n", thumb); exif_data_unref(ed); no_exifdata: /* If SOF parsing fails, then fall through to reading the JPEG data with libjpeg to get the resolution */ if( image_get_jpeg_resolution(path, &width, &height) != 0 || !width || !height ) { infile = fopen(path, "r"); if( infile ) { cinfo.err = jpeg_std_error(&jerr); jerr.error_exit = libjpeg_error_handler; jpeg_create_decompress(&cinfo); if( setjmp(setjmp_buffer) ) goto error; jpeg_stdio_src(&cinfo, infile); jpeg_read_header(&cinfo, TRUE); jpeg_start_decompress(&cinfo); width = cinfo.output_width; height = cinfo.output_height; error: jpeg_destroy_decompress(&cinfo); fclose(infile); } } //DEBUG DPRINTF(E_DEBUG, L_METADATA, " * resolution: %dx%d\n", width, height); if( !width || !height ) { free_metadata(&m, free_flags); return 0; } if( width <= 640 && height <= 480 ) m.dlna_pn = strdup("JPEG_SM"); else if( width <= 1024 && height <= 768 ) m.dlna_pn = strdup("JPEG_MED"); else if( (width <= 4096 && height <= 4096) || !GETFLAG(DLNA_STRICT_MASK) ) m.dlna_pn = strdup("JPEG_LRG"); xasprintf(&m.resolution, "%dx%d", width, height); ret = sql_exec(db, "INSERT into DETAILS" " (PATH, TITLE, SIZE, TIMESTAMP, DATE, RESOLUTION," " ROTATION, THUMBNAIL, CREATOR, DLNA_PN, MIME) " "VALUES" " (%Q, '%q', %lld, %lld, %Q, %Q, %u, %d, %Q, %Q, %Q);", path, name, (long long)file.st_size, (long long)file.st_mtime, m.date, m.resolution, m.rotation, thumb, m.creator, m.dlna_pn, m.mime); if( ret != SQLITE_OK ) { DPRINTF(E_ERROR, L_METADATA, "Error inserting details for '%s'!\n", path); ret = 0; } else { ret = sqlite3_last_insert_rowid(db); } free_metadata(&m, free_flags); return ret; }
/* 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; }
Bits CPU_Core_Dynrec_Run(void) { for (;;) { // Determine the linear address of CS:EIP PhysPt ip_point=SegPhys(cs)+reg_eip; #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) return debugCallback; #endif CodePageHandlerDynRec * chandler=0; // see if the current page is present and contains code if (GCC_UNLIKELY(MakeCodePage(ip_point,chandler))) { // page not present, throw the exception CPU_Exception(cpu.exception.which,cpu.exception.error); continue; } // page doesn't contain code or is special if (GCC_UNLIKELY(!chandler)) return CPU_Core_Normal_Run(); // find correct Dynamic Block to run CacheBlockDynRec * block=chandler->FindCacheBlock(ip_point&4095); if (!block) { // no block found, thus translate the instruction stream // unless the instruction is known to be modified if (!chandler->invalidation_map || (chandler->invalidation_map[ip_point&4095]<4)) { // translate up to 32 instructions block=CreateCacheBlock(chandler,ip_point,32); } else { // let the normal core handle this instruction to avoid zero-sized blocks Bitu old_cycles=CPU_Cycles; CPU_Cycles=1; Bits nc_retcode=CPU_Core_Normal_Run(); if (!nc_retcode) { CPU_Cycles=old_cycles-1; continue; } CPU_CycleLeft+=old_cycles; return nc_retcode; } } run_block: cache.block.running=0; // now we're ready to run the dynamic code block // BlockReturn ret=((BlockReturn (*)(void))(block->cache.start))(); BlockReturn ret=core_dynrec.runcode(block->cache.start); switch (ret) { case BR_Iret: #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) return debugCallback; #endif if (!GETFLAG(TF)) { if (GETFLAG(IF) && PIC_IRQCheck) return CBRET_NONE; break; } // trapflag is set, switch to the trap-aware decoder cpudecoder=CPU_Core_Dynrec_Trap_Run; return CBRET_NONE; case BR_Normal: // the block was exited due to a non-predictable control flow // modifying instruction (like ret) or some nontrivial cpu state // changing instruction (for example switch to/from pmode), // or the maximal number of instructions to translate was reached #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) return debugCallback; #endif break; case BR_Cycles: // cycles went negative, return from the core to handle // external events, schedule the pic... #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) return debugCallback; #endif return CBRET_NONE; case BR_CallBack: // the callback code is executed in dosbox.conf, return the callback number FillFlags(); return core_dynrec.callback; case BR_SMCBlock: // LOG_MSG("selfmodification of running block at %x:%x",SegValue(cs),reg_eip); cpu.exception.which=0; // fallthrough, let the normal core handle the block-modifying instruction case BR_Opcode: // some instruction has been encountered that could not be translated // (thus it is not part of the code block), the normal core will // handle this instruction CPU_CycleLeft+=CPU_Cycles; CPU_Cycles=1; return CPU_Core_Normal_Run(); #if (C_DEBUG) case BR_OpcodeFull: CPU_CycleLeft+=CPU_Cycles; CPU_Cycles=1; return CPU_Core_Full_Run(); #endif case BR_Link1: case BR_Link2: block=LinkBlocks(ret); if (block) goto run_block; break; default: E_Exit("Invalid return code %d", ret); } } return CBRET_NONE; }
/* CF Carry Flag -- Set on high-order bit carry or borrow; cleared otherwise. */ Bit32u get_CF(void) { switch (lflags.type) { case t_UNKNOWN: case t_INCb: case t_INCw: case t_INCd: case t_DECb: case t_DECw: case t_DECd: case t_MUL: return GETFLAG(CF); case t_ADDb: return (lf_resb<lf_var1b); case t_ADDw: return (lf_resw<lf_var1w); case t_ADDd: return (lf_resd<lf_var1d); case t_ADCb: return (lf_resb < lf_var1b) || (lflags.oldcf && (lf_resb == lf_var1b)); case t_ADCw: return (lf_resw < lf_var1w) || (lflags.oldcf && (lf_resw == lf_var1w)); case t_ADCd: return (lf_resd < lf_var1d) || (lflags.oldcf && (lf_resd == lf_var1d)); case t_SBBb: return (lf_var1b < lf_resb) || (lflags.oldcf && (lf_var2b==0xff)); case t_SBBw: return (lf_var1w < lf_resw) || (lflags.oldcf && (lf_var2w==0xffff)); case t_SBBd: return (lf_var1d < lf_resd) || (lflags.oldcf && (lf_var2d==0xffffffff)); case t_SUBb: case t_CMPb: return (lf_var1b<lf_var2b); case t_SUBw: case t_CMPw: return (lf_var1w<lf_var2w); case t_SUBd: case t_CMPd: return (lf_var1d<lf_var2d); case t_SHLb: if (lf_var2b>8) return false; else return (lf_var1b >> (8-lf_var2b)) & 1; case t_SHLw: if (lf_var2b>16) return false; else return (lf_var1w >> (16-lf_var2b)) & 1; case t_SHLd: case t_DSHLw: /* Hmm this is not correct for shift higher than 16 */ case t_DSHLd: return (lf_var1d >> (32 - lf_var2b)) & 1; case t_RCRb: case t_SHRb: return (lf_var1b >> (lf_var2b - 1)) & 1; case t_RCRw: case t_SHRw: return (lf_var1w >> (lf_var2b - 1)) & 1; case t_RCRd: case t_SHRd: case t_DSHRw: /* Hmm this is not correct for shift higher than 16 */ case t_DSHRd: return (lf_var1d >> (lf_var2b - 1)) & 1; case t_SARb: return (((Bit8s) lf_var1b) >> (lf_var2b - 1)) & 1; case t_SARw: return (((Bit16s) lf_var1w) >> (lf_var2b - 1)) & 1; case t_SARd: return (((Bit32s) lf_var1d) >> (lf_var2b - 1)) & 1; case t_NEGb: return lf_var1b; case t_NEGw: return lf_var1w; case t_NEGd: return lf_var1d; case t_ORb: case t_ORw: case t_ORd: case t_ANDb: case t_ANDw: case t_ANDd: case t_XORb: case t_XORw: case t_XORd: case t_TESTb: case t_TESTw: case t_TESTd: return false; /* Set to false */ case t_DIV: return false; /* Unkown */ default: LOG(LOG_CPU,LOG_ERROR)("get_CF Unknown %d",lflags.type); } return 0; }
void RunPC(void) { while (1) { while (CPU_Cycles > 0) { if (BIOS_HostTimeSync() && !ISR) { intPending = true; // New timer tick #ifdef WITHIRQ1 kbPending = false; #endif } #ifdef WITHIRQ1 else if (useIrq1 && !intPending && keyb_req && !ISR) kbPending=true; #endif if (GETFLAG(IF)) // (hardware) Interrupts handled if (intPending) { intPending = false; if ((Mem_Lodsb(4*8+3)|Mem_Lodsb(4*0x1c+3)) != 0xf0) // And Int 8 or 1C replaced { ISR = 1; CPU_HW_Interrupt(8); // Setup executing Int 8 (IRQ0) } } #ifdef WITHIRQ1 else if (kbPending && Mem_Lodsb(4 * 9 + 3) != 0xf0) { kbPending = false; keyb_req = false; ISR = 2; CPU_HW_Interrupt(9); } #endif else if (mouse_event_type) CPU_HW_Interrupt(0x74); // Setup executing Int 74 (Mouse) Bits ret = (*cpudecoder)(); if (ret < 0) return; if (ret > 0 && (*CallBack_Handlers[ret])()) return; } GFX_Events(); Bit32u mSecsNew = GetTickCount(); if (mSecsNew >= prevWinRefresh+40) // 25 refreshes per second { prevWinRefresh = mSecsNew; VGA_VerticalTimer(); } if (mSecsNew <= mSecsLast+55) // To be real save??? { LPT_CheckTimeOuts(mSecsNew); Sleep(idleCount >= idleTrigger ? 2: 0); // If idleTrigger or more repeated idle keyboard requests or int 28h called, sleep fixed (CPU usage drops down) if (idleCount > idleTrigger && CPU_CycleMax > CPU_CycleLow) CPU_CycleMax -= 100; // Decrease cycles else if (idleCount <= idleTrigger && CPU_CycleMax < CPU_CycleHigh) CPU_CycleMax += 1000; // Fire up again } mSecsLast = mSecsNew; idleCount = 0; CPU_Cycles = CPU_CycleMax; } }
int add_pinhole(const char * ifname, const char * rem_host, unsigned short rem_port, const char * int_client, unsigned short int_port, int proto, const char * desc, unsigned int timestamp) { int uid; struct pfioc_rule pcr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; #endif if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } memset(&pcr, 0, sizeof(pcr)); strlcpy(pcr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m"); return -1; } else { pcr.pool_ticket = pp.ticket; #else { #endif pcr.rule.direction = PF_IN; pcr.rule.action = PF_PASS; pcr.rule.af = AF_INET6; #ifdef PF_NEWSTYLE pcr.rule.nat.addr.type = PF_ADDR_NONE; pcr.rule.rdr.addr.type = PF_ADDR_NONE; #endif #ifdef USE_IFNAME_IN_RULES if(ifname) strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ); #endif pcr.rule.proto = proto; pcr.rule.quick = 1;/*(GETFLAG(PFNOQUICKRULESMASK))?0:1;*/ pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/ /* see the discussion on the forum : * http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=638 */ pcr.rule.flags = TH_SYN; pcr.rule.flagset = (TH_SYN|TH_ACK); #ifdef PFRULE_HAS_RTABLEID pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */ #endif #ifdef PFRULE_HAS_ONRDOMAIN pcr.rule.onrdomain = -1; /* first appeared in OpenBSD 5.0 */ #endif pcr.rule.keep_state = 1; uid = next_uid; snprintf(pcr.rule.label, PF_RULE_LABEL_SIZE, PINEHOLE_LABEL_FORMAT, uid, timestamp, desc); if(queue) strlcpy(pcr.rule.qname, queue, PF_QNAME_SIZE); if(tag) strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE); if(rem_port) { pcr.rule.src.port_op = PF_OP_EQ; pcr.rule.src.port[0] = htons(rem_port); } if(rem_host && rem_host[0] != '\0' && rem_host[0] != '*') { pcr.rule.src.addr.type = PF_ADDR_ADDRMASK; if(inet_pton(AF_INET6, rem_host, &pcr.rule.src.addr.v.a.addr.v6) != 1) { syslog(LOG_ERR, "inet_pton(%s) failed", rem_host); } memset(&pcr.rule.src.addr.v.a.mask.addr8, 255, 16); } pcr.rule.dst.port_op = PF_OP_EQ; pcr.rule.dst.port[0] = htons(int_port); pcr.rule.dst.addr.type = PF_ADDR_ADDRMASK; if(inet_pton(AF_INET6, int_client, &pcr.rule.dst.addr.v.a.addr.v6) != 1) { syslog(LOG_ERR, "inet_pton(%s) failed", int_client); } memset(&pcr.rule.dst.addr.v.a.mask.addr8, 255, 16); if(ifname) strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ); pcr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); return -1; } else { pcr.action = PF_CHANGE_ADD_TAIL; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m"); return -1; } } } if(++next_uid >= 65535) { next_uid = 1; } return uid; } int find_pinhole(const char * ifname, const char * rem_host, unsigned short rem_port, const char * int_client, unsigned short int_port, int proto, char *desc, int desc_len, unsigned int * timestamp) { int uid; unsigned int ts; int i, n; struct pfioc_rule pr; struct in6_addr saddr; struct in6_addr daddr; UNUSED(ifname); if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } if(rem_host && (rem_host[0] != '\0')) { inet_pton(AF_INET6, rem_host, &saddr); } else { memset(&saddr, 0, sizeof(struct in6_addr)); } inet_pton(AF_INET6, int_client, &daddr); memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE pr.rule.action = PF_PASS; #endif if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); return -1; } n = pr.nr; for(i=0; i<n; i++) { pr.nr = i; if(ioctl(dev, DIOCGETRULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); return -1; } if((proto == pr.rule.proto) && (rem_port == ntohs(pr.rule.src.port[0])) && (0 == memcmp(&saddr, &pr.rule.src.addr.v.a.addr.v6, sizeof(struct in6_addr))) && (int_port == ntohs(pr.rule.dst.port[0])) && (0 == memcmp(&daddr, &pr.rule.dst.addr.v.a.addr.v6, sizeof(struct in6_addr)))) { if(sscanf(pr.rule.label, PINEHOLE_LABEL_FORMAT_SKIPDESC, &uid, &ts) != 2) { syslog(LOG_DEBUG, "rule with label '%s' is not a IGD pinhole", pr.rule.label); continue; } if(timestamp) *timestamp = ts; if(desc) { char * p = strchr(pr.rule.label, ':'); if(p) { p += 2; strlcpy(desc, p, desc_len); } } return uid; } } return -2; }
/* add_redirect_rule2() : * create a rdr rule */ int add_redirect_rule2(const char * ifname, const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, int proto, const char * desc, unsigned int timestamp) { int r; struct pfioc_rule pcr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; struct pf_pooladdr *a; #endif if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } r = 0; memset(&pcr, 0, sizeof(pcr)); strlcpy(pcr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m"); r = -1; } else { pcr.pool_ticket = pp.ticket; #else if(1) { pcr.rule.direction = PF_IN; /*pcr.rule.src.addr.type = PF_ADDR_NONE;*/ pcr.rule.src.addr.type = PF_ADDR_ADDRMASK; pcr.rule.dst.addr.type = PF_ADDR_ADDRMASK; pcr.rule.nat.addr.type = PF_ADDR_NONE; pcr.rule.rdr.addr.type = PF_ADDR_ADDRMASK; #endif #ifdef __APPLE__ pcr.rule.dst.xport.range.op = PF_OP_EQ; pcr.rule.dst.xport.range.port[0] = htons(eport); pcr.rule.dst.xport.range.port[1] = htons(eport); #else pcr.rule.dst.port_op = PF_OP_EQ; pcr.rule.dst.port[0] = htons(eport); pcr.rule.dst.port[1] = htons(eport); #endif #ifndef PF_NEWSTYLE pcr.rule.action = PF_RDR; #ifndef PF_ENABLE_FILTER_RULES pcr.rule.natpass = 1; #else pcr.rule.natpass = 0; #endif #else #ifndef PF_ENABLE_FILTER_RULES pcr.rule.action = PF_PASS; #else pcr.rule.action = PF_MATCH; #endif #endif pcr.rule.af = AF_INET; #ifdef USE_IFNAME_IN_RULES if(ifname) strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ); #endif pcr.rule.proto = proto; pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/ #ifdef PFRULE_HAS_RTABLEID pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */ #endif #ifdef PFRULE_HAS_ONRDOMAIN pcr.rule.onrdomain = -1; /* first appeared in OpenBSD 5.0 */ #endif pcr.rule.quick = 1; pcr.rule.keep_state = PF_STATE_NORMAL; if(tag) strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE); strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE); if(rhost && rhost[0] != '\0' && rhost[0] != '*') { inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr); pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); } #ifndef PF_NEWSTYLE pcr.rule.rpool.proxy_port[0] = iport; pcr.rule.rpool.proxy_port[1] = iport; TAILQ_INIT(&pcr.rule.rpool.list); a = calloc(1, sizeof(struct pf_pooladdr)); inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr); a->addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); TAILQ_INSERT_TAIL(&pcr.rule.rpool.list, a, entries); memcpy(&pp.addr, a, sizeof(struct pf_pooladdr)); if(ioctl(dev, DIOCADDADDR, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCADDADDR, ...): %m"); r = -1; } else { #else pcr.rule.rdr.proxy_port[0] = iport; pcr.rule.rdr.proxy_port[1] = iport; inet_pton(AF_INET, iaddr, &pcr.rule.rdr.addr.v.a.addr.v4.s_addr); pcr.rule.rdr.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); if(1) { #endif pcr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); r = -1; } else { pcr.action = PF_CHANGE_ADD_TAIL; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m"); r = -1; } } } #ifndef PF_NEWSTYLE free(a); #endif } if(r == 0 && timestamp > 0) { struct timestamp_entry * tmp; tmp = malloc(sizeof(struct timestamp_entry)); if(tmp) { tmp->next = timestamp_list; tmp->timestamp = timestamp; tmp->eport = eport; tmp->protocol = (short)proto; timestamp_list = tmp; } } return r; } /* thanks to Seth Mos for this function */ int add_filter_rule2(const char * ifname, const char * rhost, const char * iaddr, unsigned short eport, unsigned short iport, int proto, const char * desc) { #ifndef PF_ENABLE_FILTER_RULES UNUSED(ifname); UNUSED(rhost); UNUSED(iaddr); UNUSED(eport); UNUSED(iport); UNUSED(proto); UNUSED(desc); return 0; #else int r; struct pfioc_rule pcr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; #endif #ifndef USE_IFNAME_IN_RULES UNUSED(ifname); #endif UNUSED(eport); if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } r = 0; memset(&pcr, 0, sizeof(pcr)); strlcpy(pcr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m"); r = -1; } else { pcr.pool_ticket = pp.ticket; #else if(1) { #endif pcr.rule.dst.port_op = PF_OP_EQ; pcr.rule.dst.port[0] = htons(iport); pcr.rule.direction = PF_IN; pcr.rule.action = PF_PASS; pcr.rule.af = AF_INET; #ifdef USE_IFNAME_IN_RULES if(ifname) strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ); #endif pcr.rule.proto = proto; pcr.rule.quick = (GETFLAG(PFNOQUICKRULESMASK))?0:1; pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/ /* see the discussion on the forum : * http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=638 */ pcr.rule.flags = TH_SYN; pcr.rule.flagset = (TH_SYN|TH_ACK); #ifdef PFRULE_HAS_RTABLEID pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */ #endif #ifdef PFRULE_HAS_ONRDOMAIN pcr.rule.onrdomain = -1; /* first appeared in OpenBSD 5.0 */ #endif pcr.rule.keep_state = 1; strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE); if(queue) strlcpy(pcr.rule.qname, queue, PF_QNAME_SIZE); if(tag) strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE); if(rhost && rhost[0] != '\0' && rhost[0] != '*') { inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr); pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); } /* we want any - iaddr port = # keep state label */ inet_pton(AF_INET, iaddr, &pcr.rule.dst.addr.v.a.addr.v4.s_addr); pcr.rule.dst.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); #ifndef PF_NEWSTYLE pcr.rule.rpool.proxy_port[0] = iport; pcr.rule.rpool.proxy_port[1] = iport; TAILQ_INIT(&pcr.rule.rpool.list); #endif if(1) { pcr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); r = -1; } else { pcr.action = PF_CHANGE_ADD_TAIL; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m"); r = -1; } } } } return r; #endif } /* get_redirect_rule() * return value : 0 success (found) * -1 = error or rule not found */ int get_redirect_rule(const char * ifname, unsigned short eport, int proto, char * iaddr, int iaddrlen, unsigned short * iport, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * timestamp, u_int64_t * packets, u_int64_t * bytes) { int i, n; struct pfioc_rule pr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; #endif UNUSED(ifname); if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE pr.rule.action = PF_RDR; #endif if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); goto error; } n = pr.nr; for(i=0; i<n; i++) { pr.nr = i; if(ioctl(dev, DIOCGETRULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); goto error; } #ifdef __APPLE__ if( (eport == ntohs(pr.rule.dst.xport.range.port[0])) && (eport == ntohs(pr.rule.dst.xport.range.port[1])) #else if( (eport == ntohs(pr.rule.dst.port[0])) && (eport == ntohs(pr.rule.dst.port[1])) #endif && (pr.rule.proto == proto) ) { #ifndef PF_NEWSTYLE *iport = pr.rule.rpool.proxy_port[0]; #else *iport = pr.rule.rdr.proxy_port[0]; #endif if(desc) strlcpy(desc, pr.rule.label, desclen); #ifdef PFRULE_INOUT_COUNTS if(packets) *packets = pr.rule.packets[0] + pr.rule.packets[1]; if(bytes) *bytes = pr.rule.bytes[0] + pr.rule.bytes[1]; #else if(packets) *packets = pr.rule.packets; if(bytes) *bytes = pr.rule.bytes; #endif #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); pp.r_action = PF_RDR; pp.r_num = i; pp.ticket = pr.ticket; if(ioctl(dev, DIOCGETADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m"); goto error; } if(pp.nr != 1) { syslog(LOG_NOTICE, "No address associated with pf rule"); goto error; } pp.nr = 0; /* first */ if(ioctl(dev, DIOCGETADDR, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m"); goto error; } inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr, iaddr, iaddrlen); #else inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr, iaddr, iaddrlen); #endif if(rhost && rhostlen > 0) { if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0) { rhost[0] = '\0'; /* empty string */ } else { inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr, rhost, rhostlen); } } if(timestamp) *timestamp = get_timestamp(eport, proto); return 0; } } error: return -1; } static int priv_delete_redirect_rule(const char * ifname, unsigned short eport, int proto, unsigned short * iport, in_addr_t * iaddr) { int i, n; struct pfioc_rule pr; UNUSED(ifname); if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE pr.rule.action = PF_RDR; #endif if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); goto error; } n = pr.nr; for(i=0; i<n; i++) { pr.nr = i; if(ioctl(dev, DIOCGETRULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); goto error; } #ifdef __APPLE__ if( (eport == ntohs(pr.rule.dst.xport.range.port[0])) && (eport == ntohs(pr.rule.dst.xport.range.port[1])) #else if( (eport == ntohs(pr.rule.dst.port[0])) && (eport == ntohs(pr.rule.dst.port[1])) #endif && (pr.rule.proto == proto) ) { /* retrieve iport in order to remove filter rule */ #ifndef PF_NEWSTYLE if(iport) *iport = pr.rule.rpool.proxy_port[0]; if(iaddr) { /* retrieve internal address */ struct pfioc_pooladdr pp; memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); pp.r_action = PF_RDR; pp.r_num = i; pp.ticket = pr.ticket; if(ioctl(dev, DIOCGETADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m"); goto error; } if(pp.nr != 1) { syslog(LOG_NOTICE, "No address associated with pf rule"); goto error; } pp.nr = 0; /* first */ if(ioctl(dev, DIOCGETADDR, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m"); goto error; } *iaddr = pp.addr.addr.v.a.addr.v4.s_addr; } #else if(iport) *iport = pr.rule.rdr.proxy_port[0]; if(iaddr) { /* retrieve internal address */ *iaddr = pr.rule.rdr.addr.v.a.addr.v4.s_addr; } #endif pr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); goto error; } pr.action = PF_CHANGE_REMOVE; pr.nr = i; if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m"); goto error; } remove_timestamp_entry(eport, proto); return 0; } } error: return -1; }
sval_t bst_find(skey_t k, node_t** pred, operation_t** pred_op, node_t** curr, operation_t** curr_op, node_t* aux_root, node_t* root){ sval_t result; skey_t curr_key; node_t* next; node_t* last_right; operation_t* last_right_op; retry: PARSE_TRY(); result = NOT_FOUND_R; *curr = aux_root; *curr_op = (*curr)->op; if(GETFLAG(*curr_op) != STATE_OP_NONE){ #ifdef __tile__ MEM_BARRIER; #endif if (aux_root == root){ bst_help_child_cas((operation_t*)UNFLAG(*curr_op), *curr, root); goto retry; } else { return ABORT; } } next = (node_t*) (*curr)->right; last_right = *curr; last_right_op = *curr_op; while (!ISNULL(next)){ *pred = *curr; *pred_op = *curr_op; *curr = next; *curr_op = (*curr)->op; if(GETFLAG(*curr_op) != STATE_OP_NONE){ bst_help(*pred, *pred_op, *curr, *curr_op, root); goto retry; } curr_key = (*curr)->key; if(k < curr_key){ result = NOT_FOUND_L; next = (node_t*) (*curr)->left; } else if(k > curr_key) { result = NOT_FOUND_R; next = (node_t*) (*curr)->right; last_right = *curr; last_right_op = *curr_op; } else{ result = (*curr)->value; break; } } if ((!(result & val_mask)) && (last_right_op != last_right->op)) { goto retry; } if ((*curr)->op != *curr_op){ goto retry; } return result; }
search_res_t bst_find(bst_key_t k, node_t** pred, operation_t** pred_op, node_t** curr, operation_t** curr_op, node_t* aux_root, node_t* root){ search_res_t result; bst_key_t curr_key; node_t* next; node_t* last_right; operation_t* last_right_op; retry: result = NOT_FOUND_R; *curr = aux_root; *curr_op = (*curr)->op; if(GETFLAG(*curr_op) != STATE_OP_NONE){ if (aux_root == root){ bst_help_child_cas((operation_t*)UNFLAG(*curr_op), *curr, root); goto retry; } else { return ABORT; } } next = (*curr)->right; last_right = *curr; last_right_op = *curr_op; while (!ISNULL(next)){ *pred = *curr; *pred_op = *curr_op; *curr = next; *curr_op = (*curr)->op; if(GETFLAG(*curr_op) != STATE_OP_NONE){ bst_help(*pred, *pred_op, *curr, *curr_op, root); goto retry; } curr_key = (*curr)->key; if(k < curr_key){ result = NOT_FOUND_L; next = (*curr)->left; } else if(k > curr_key) { result = NOT_FOUND_R; next = (*curr)->right; last_right = *curr; last_right_op = *curr_op; } else{ result = FOUND; break; } } if ((result != FOUND) && (last_right_op != last_right->op)) { goto retry; } if ((*curr)->op != *curr_op){ goto retry; } return result; }
/* AF Adjust flag -- Set on carry from or borrow to the low order four bits of AL; cleared otherwise. Used for decimal arithmetic. */ Bit32u get_AF(void) { Bitu type=lflags.type; switch (type) { case t_UNKNOWN: return GETFLAG(AF); case t_ADDb: case t_ADCb: case t_SBBb: case t_SUBb: case t_CMPb: return ((lf_var1b ^ lf_var2b) ^ lf_resb) & 0x10; case t_ADDw: case t_ADCw: case t_SBBw: case t_SUBw: case t_CMPw: return ((lf_var1w ^ lf_var2w) ^ lf_resw) & 0x10; case t_ADCd: case t_ADDd: case t_SBBd: case t_SUBd: case t_CMPd: return ((lf_var1d ^ lf_var2d) ^ lf_resd) & 0x10; case t_INCb: return (lf_resb & 0x0f) == 0; case t_INCw: return (lf_resw & 0x0f) == 0; case t_INCd: return (lf_resd & 0x0f) == 0; case t_DECb: return (lf_resb & 0x0f) == 0x0f; case t_DECw: return (lf_resw & 0x0f) == 0x0f; case t_DECd: return (lf_resd & 0x0f) == 0x0f; case t_NEGb: return lf_var1b & 0x0f; case t_NEGw: return lf_var1w & 0x0f; case t_NEGd: return lf_var1d & 0x0f; case t_SHLb: case t_SHRb: case t_SARb: return lf_var2b & 0x1f; case t_SHLw: case t_SHRw: case t_SARw: return lf_var2w & 0x1f; case t_SHLd: case t_SHRd: case t_SARd: return lf_var2d & 0x1f; case t_ORb: case t_ORw: case t_ORd: case t_ANDb: case t_ANDw: case t_ANDd: case t_XORb: case t_XORw: case t_XORd: case t_TESTb: case t_TESTw: case t_TESTd: case t_DSHLw: case t_DSHLd: case t_DSHRw: case t_DSHRd: case t_DIV: case t_MUL: return false; /* Unkown */ default: LOG(LOG_CPU,LOG_ERROR)("get_AF Unknown %d",lflags.type); } return 0; }
bst_search_result_t* bst_find(bst_key_t k, node_t* aux_root, node_t* root, int id){ //fprintf(stderr, "bst find\n"); bst_search_result_t* my_result = my_search_result[id]; // search_res_t result; bst_key_t curr_key; node_t* next; node_t* last_right; operation_t* last_right_op; retry: my_result->result = NOT_FOUND_R; my_result->curr = aux_root; my_result->curr_op = my_result->curr->op; if(GETFLAG(my_result->curr_op) != STATE_OP_NONE){ fprintf(stderr, "Shouldn't be here\n"); //root is now a pointer to a node, not a node if (aux_root == root){ bst_help_child_cas((operation_t*)UNFLAG(my_result->curr_op), my_result->curr/*, aux_root*/); goto retry; } else { my_result->result = ABORT; return my_result; } } next = my_result->curr->right; last_right = my_result->curr; last_right_op = my_result->curr_op; while (!ISNULL(next)){ my_result->pred = my_result->curr; my_result->pred_op = my_result->curr_op; my_result->curr = next; my_result->curr_op = (my_result->curr)->op; if(GETFLAG(my_result->curr_op) != STATE_OP_NONE){ fprintf(stderr, "Shouldn't be here 2\n"); bst_help(my_result->pred, my_result->pred_op, my_result->curr, my_result->curr_op/*, aux_root*/); goto retry; } curr_key = (my_result->curr)->key; if(k < curr_key){ my_result->result = NOT_FOUND_L; next = (my_result->curr)->left; } else if(k > curr_key) { my_result->result = NOT_FOUND_R; next = (my_result->curr)->right; last_right = my_result->curr; last_right_op = my_result->curr_op; } else{ my_result->result = FOUND; break; } } if ((my_result->result != FOUND) && (last_right_op != last_right->op)) { fprintf(stderr, "Shouldn't be here 3\n"); goto retry; } if ((my_result->curr)->op != my_result->curr_op){ fprintf(stderr, "Shouldn't be here 4\n"); goto retry; } return my_result; }
/* SF Sign Flag -- Set equal to high-order bit of result (0 is positive, 1 if negative). */ Bit32u get_SF(void) { Bitu type=lflags.type; switch (type) { case t_UNKNOWN: return GETFLAG(SF); case t_ADDb: case t_ORb: case t_ADCb: case t_SBBb: case t_ANDb: case t_XORb: case t_SUBb: case t_CMPb: case t_INCb: case t_DECb: case t_TESTb: case t_SHLb: case t_SHRb: case t_SARb: case t_NEGb: return (lf_resb&0x80); case t_ADDw: case t_ORw: case t_ADCw: case t_SBBw: case t_ANDw: case t_XORw: case t_SUBw: case t_CMPw: case t_INCw: case t_DECw: case t_TESTw: case t_SHLw: case t_SHRw: case t_SARw: case t_DSHLw: case t_DSHRw: case t_NEGw: return (lf_resw&0x8000); case t_ADDd: case t_ORd: case t_ADCd: case t_SBBd: case t_ANDd: case t_XORd: case t_SUBd: case t_CMPd: case t_INCd: case t_DECd: case t_TESTd: case t_SHLd: case t_SHRd: case t_SARd: case t_DSHLd: case t_DSHRd: case t_NEGd: return (lf_resd&0x80000000); case t_DIV: case t_MUL: return false; /* Unkown */ default: LOG(LOG_CPU,LOG_ERROR)("get_SF Unkown %d",lflags.type); } return false; }
static void check_db(sqlite3 *db, int new_db, pid_t *scanner_pid) { char cmd[PATH_MAX*2]; int ret; #ifdef MD_CHECK_MP struct media_dir_s *media_path = NULL; char **result; int i, rows = 0; if (!new_db) { /* Check if any new media dirs appeared */ media_path = media_dirs; while (media_path) { ret = sql_get_int_field(db, "SELECT TIMESTAMP from DETAILS where PATH = %Q", media_path->path); if (ret != media_path->types) { ret = 1; goto rescan; } media_path = media_path->next; } /* Check if any media dirs disappeared */ sql_get_table(db, "SELECT VALUE from SETTINGS where KEY = 'media_dir'", &result, &rows, NULL); for (i=1; i <= rows; i++) { media_path = media_dirs; while (media_path) { if (strcmp(result[i], media_path->path) == 0) break; media_path = media_path->next; } if (!media_path) { ret = 2; sqlite3_free_table(result); goto rescan; } } sqlite3_free_table(result); } #endif ret = db_upgrade(db); if ((ret != 0) || (GETFLAG(UPDATE_SCAN_MASK))) { if (ret != 0) { #ifdef MD_CHECK_MP rescan: #endif if (ret < 0) DPRINTF(E_WARN, L_GENERAL, "Creating new database at %s/files.db\n", db_path); #ifdef MD_CHECK_MP else if (ret == 1) DPRINTF(E_WARN, L_GENERAL, "New media_dir detected; rescanning...\n"); else if (ret == 2) DPRINTF(E_WARN, L_GENERAL, "Removed media_dir detected; rescanning...\n"); #endif else DPRINTF(E_WARN, L_GENERAL, "Database version mismatch; need to recreate...\n"); sqlite3_close(db); snprintf(cmd, sizeof(cmd), "rm -rf %s/files.db %s/art_cache", db_path, db_path); if (system(cmd) != 0) DPRINTF(E_FATAL, L_GENERAL, "Failed to clean old file cache! Exiting...\n"); open_db(&db); if (CreateDatabase() != 0) DPRINTF(E_FATAL, L_GENERAL, "ERROR: Failed to create sqlite database! Exiting...\n"); } #if USE_FORK scanning = 1; sqlite3_close(db); *scanner_pid = process_fork(); open_db(&db); if (*scanner_pid == 0) /* child (scanner) process */ { start_scanner(); sqlite3_close(db); log_close(); freeoptions(); exit(EXIT_SUCCESS); } else if (*scanner_pid < 0) { start_scanner(); } #else start_scanner(); #endif } }
Bit32u get_OF(void) { Bitu type=lflags.type; switch (type) { case t_UNKNOWN: case t_MUL: return GETFLAG(OF); case t_ADDb: case t_ADCb: return ((lf_var1b ^ lf_var2b ^ 0x80) & (lf_resb ^ lf_var2b)) & 0x80; case t_ADDw: case t_ADCw: return ((lf_var1w ^ lf_var2w ^ 0x8000) & (lf_resw ^ lf_var2w)) & 0x8000; case t_ADDd: case t_ADCd: return ((lf_var1d ^ lf_var2d ^ 0x80000000) & (lf_resd ^ lf_var2d)) & 0x80000000; case t_SBBb: case t_SUBb: case t_CMPb: return ((lf_var1b ^ lf_var2b) & (lf_var1b ^ lf_resb)) & 0x80; case t_SBBw: case t_SUBw: case t_CMPw: return ((lf_var1w ^ lf_var2w) & (lf_var1w ^ lf_resw)) & 0x8000; case t_SBBd: case t_SUBd: case t_CMPd: return ((lf_var1d ^ lf_var2d) & (lf_var1d ^ lf_resd)) & 0x80000000; case t_INCb: return (lf_resb == 0x80); case t_INCw: return (lf_resw == 0x8000); case t_INCd: return (lf_resd == 0x80000000); case t_DECb: return (lf_resb == 0x7f); case t_DECw: return (lf_resw == 0x7fff); case t_DECd: return (lf_resd == 0x7fffffff); case t_NEGb: return (lf_var1b == 0x80); case t_NEGw: return (lf_var1w == 0x8000); case t_NEGd: return (lf_var1d == 0x80000000); case t_SHLb: return (lf_resb ^ lf_var1b) & 0x80; case t_SHLw: case t_DSHRw: case t_DSHLw: return (lf_resw ^ lf_var1w) & 0x8000; case t_SHLd: case t_DSHRd: case t_DSHLd: return (lf_resd ^ lf_var1d) & 0x80000000; case t_SHRb: if ((lf_var2b&0x1f)==1) return (lf_var1b > 0x80); else return false; case t_SHRw: if ((lf_var2b&0x1f)==1) return (lf_var1w > 0x8000); else return false; case t_SHRd: if ((lf_var2b&0x1f)==1) return (lf_var1d > 0x80000000); else return false; case t_ORb: case t_ORw: case t_ORd: case t_ANDb: case t_ANDw: case t_ANDd: case t_XORb: case t_XORw: case t_XORd: case t_TESTb: case t_TESTw: case t_TESTd: case t_SARb: case t_SARw: case t_SARd: return false; /* Return false */ case t_DIV: return false; /* Unkown */ default: LOG(LOG_CPU,LOG_ERROR)("get_OF Unkown %d",lflags.type); } return false; }
/* process HTTP or SSDP requests */ int main(int argc, char * * argv) { int i; int sudp = -1, shttpl = -1; int snotify[MAX_LAN_ADDR]; LIST_HEAD(httplisthead, upnphttp) upnphttphead; struct upnphttp * e = 0; struct upnphttp * next; fd_set readset; /* for select() */ fd_set writeset; struct timeval timeout, timeofday, lastnotifytime = {0, 0}, lastupdatetime = {0, 0}; int max_fd = -1; int last_changecnt = 0; short int new_db = 0; pid_t scanner_pid = 0; pthread_t inotify_thread = 0; #ifdef TIVO_SUPPORT unsigned short int beacon_interval = 5; int sbeacon = -1; struct sockaddr_in tivo_bcast; struct timeval lastbeacontime = {0, 0}; #endif if(init(argc, argv) != 0) return 1; #ifdef READYNAS DPRINTF(E_WARN, L_GENERAL, "Starting ReadyDLNA version " MINIDLNA_VERSION ".\n"); unlink("/ramfs/.upnp-av_scan"); #else DPRINTF(E_WARN, L_GENERAL, "Starting MiniDLNA version " MINIDLNA_VERSION " [SQLite %s].\n", sqlite3_libversion()); if( !sqlite3_threadsafe() ) { DPRINTF(E_ERROR, L_GENERAL, "SQLite library is not threadsafe! " "Scanning must be finished before file serving can begin, " "and inotify will be disabled.\n"); } if( sqlite3_libversion_number() < 3005001 ) { DPRINTF(E_WARN, L_GENERAL, "SQLite library is old. Please use version 3.5.1 or newer.\n"); } #endif LIST_INIT(&upnphttphead); if( access(DB_PATH, F_OK) != 0 ) { char *db_path = strdup(DB_PATH); make_dir(db_path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); free(db_path); new_db = 1; } if( sqlite3_open(DB_PATH "/files.db", &db) != SQLITE_OK ) { DPRINTF(E_FATAL, L_GENERAL, "ERROR: Failed to open sqlite database! Exiting...\n"); } else { sqlite3_busy_timeout(db, 5000); if( !new_db ) { updateID = sql_get_int_field(db, "SELECT UPDATE_ID from SETTINGS"); } if( sql_get_int_field(db, "pragma user_version") != DB_VERSION ) { if( new_db ) { DPRINTF(E_WARN, L_GENERAL, "Creating new database...\n"); } else { DPRINTF(E_WARN, L_GENERAL, "Database version mismatch; need to recreate...\n"); } sqlite3_close(db); unlink(DB_PATH "/files.db"); //system("rm -rf " DB_PATH "/art_cache"); sqlite3_open(DB_PATH "/files.db", &db); sqlite3_busy_timeout(db, 5000); if( CreateDatabase() != 0 ) { DPRINTF(E_FATAL, L_GENERAL, "ERROR: Failed to create sqlite database! Exiting...\n"); } #if USE_FORK scanning = 1; sqlite3_close(db); scanner_pid = fork(); sqlite3_open(DB_PATH "/files.db", &db); sqlite3_busy_timeout(db, 5000); if( !scanner_pid ) // child (scanner) process { start_scanner(); sqlite3_close(db); exit(EXIT_SUCCESS); } #else start_scanner(); #endif } if( sqlite3_threadsafe() && sqlite3_libversion_number() >= 3005001 && GETFLAG(INOTIFY_MASK) && pthread_create(&inotify_thread, NULL, start_inotify, NULL) ) { DPRINTF(E_FATAL, L_GENERAL, "ERROR: pthread_create() failed for start_inotify.\n"); } } sudp = OpenAndConfSSDPReceiveSocket(n_lan_addr, lan_addr); if(sudp < 0) { DPRINTF(E_FATAL, L_GENERAL, "Failed to open socket for receiving SSDP. EXITING\n"); } /* open socket for HTTP connections. Listen on the 1st LAN address */ shttpl = OpenAndConfHTTPSocket(runtime_vars.port); if(shttpl < 0) { DPRINTF(E_FATAL, L_GENERAL, "Failed to open socket for HTTP. EXITING\n"); } DPRINTF(E_WARN, L_GENERAL, "HTTP listening on port %d\n", runtime_vars.port); /* open socket for sending notifications */ if(OpenAndConfSSDPNotifySockets(snotify) < 0) { DPRINTF(E_FATAL, L_GENERAL, "Failed to open sockets for sending SSDP notify " "messages. EXITING\n"); } #ifdef TIVO_SUPPORT if( GETFLAG(TIVO_MASK) ) { DPRINTF(E_WARN, L_GENERAL, "TiVo support is enabled.\n"); /* Add TiVo-specific randomize function to sqlite */ if( sqlite3_create_function(db, "tivorandom", 1, SQLITE_UTF8, NULL, &TiVoRandomSeedFunc, NULL, NULL) != SQLITE_OK ) { DPRINTF(E_ERROR, L_TIVO, "ERROR: Failed to add sqlite randomize function for TiVo!\n"); } /* open socket for sending Tivo notifications */ sbeacon = OpenAndConfTivoBeaconSocket(); if(sbeacon < 0) { DPRINTF(E_FATAL, L_GENERAL, "Failed to open sockets for sending Tivo beacon notify " "messages. EXITING\n"); } tivo_bcast.sin_family = AF_INET; tivo_bcast.sin_addr.s_addr = htonl(getBcastAddress()); tivo_bcast.sin_port = htons(2190); } else { sbeacon = -1; } #endif SendSSDPGoodbye(snotify, n_lan_addr); /* main loop */ while(!quitting) { /* Check if we need to send SSDP NOTIFY messages and do it if * needed */ if(gettimeofday(&timeofday, 0) < 0) { DPRINTF(E_ERROR, L_GENERAL, "gettimeofday(): %s\n", strerror(errno)); timeout.tv_sec = runtime_vars.notify_interval; timeout.tv_usec = 0; } else { /* the comparaison is not very precise but who cares ? */ if(timeofday.tv_sec >= (lastnotifytime.tv_sec + runtime_vars.notify_interval)) { SendSSDPNotifies2(snotify, (unsigned short)runtime_vars.port, (runtime_vars.notify_interval << 1)+10); memcpy(&lastnotifytime, &timeofday, sizeof(struct timeval)); timeout.tv_sec = runtime_vars.notify_interval; timeout.tv_usec = 0; } else { timeout.tv_sec = lastnotifytime.tv_sec + runtime_vars.notify_interval - timeofday.tv_sec; if(timeofday.tv_usec > lastnotifytime.tv_usec) { timeout.tv_usec = 1000000 + lastnotifytime.tv_usec - timeofday.tv_usec; timeout.tv_sec--; } else { timeout.tv_usec = lastnotifytime.tv_usec - timeofday.tv_usec; } } #ifdef TIVO_SUPPORT if( GETFLAG(TIVO_MASK) ) { if(timeofday.tv_sec >= (lastbeacontime.tv_sec + beacon_interval)) { sendBeaconMessage(sbeacon, &tivo_bcast, sizeof(struct sockaddr_in), 1); memcpy(&lastbeacontime, &timeofday, sizeof(struct timeval)); if( timeout.tv_sec > beacon_interval ) { timeout.tv_sec = beacon_interval; timeout.tv_usec = 0; } /* Beacons should be sent every 5 seconds or so for the first minute, * then every minute or so thereafter. */ if( beacon_interval == 5 && (timeofday.tv_sec - startup_time) > 60 ) { beacon_interval = 60; } } else if( timeout.tv_sec > (lastbeacontime.tv_sec + beacon_interval + 1 - timeofday.tv_sec) ) { timeout.tv_sec = lastbeacontime.tv_sec + beacon_interval - timeofday.tv_sec; } } #endif } if( scanning ) { if( !scanner_pid || kill(scanner_pid, 0) ) scanning = 0; } /* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */ FD_ZERO(&readset); if (sudp >= 0) { FD_SET(sudp, &readset); max_fd = MAX( max_fd, sudp); } if (shttpl >= 0) { FD_SET(shttpl, &readset); max_fd = MAX( max_fd, shttpl); } 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)) { FD_SET(e->socket, &readset); max_fd = MAX( max_fd, e->socket); i++; } } /* for debug */ #ifdef DEBUG if(i > 1) { DPRINTF(E_DEBUG, L_GENERAL, "%d active incoming HTTP connections\n", i); } #endif FD_ZERO(&writeset); upnpevents_selectfds(&readset, &writeset, &max_fd); if(select(max_fd+1, &readset, &writeset, 0, &timeout) < 0) { if(quitting) goto shutdown; DPRINTF(E_ERROR, L_GENERAL, "select(all): %s\n", strerror(errno)); DPRINTF(E_FATAL, L_GENERAL, "Failed to select open sockets. EXITING\n"); } upnpevents_processfds(&readset, &writeset); /* process SSDP packets */ if(sudp >= 0 && FD_ISSET(sudp, &readset)) { /*DPRINTF(E_DEBUG, L_GENERAL, "Received UDP Packet\n");*/ ProcessSSDPRequest(sudp, (unsigned short)runtime_vars.port); } /* increment SystemUpdateID if the content database has changed, * and if there is an active HTTP connection, at most once every 2 seconds */ if( i && (time(NULL) >= (lastupdatetime.tv_sec + 2)) ) { if( sqlite3_total_changes(db) != last_changecnt ) { updateID++; last_changecnt = sqlite3_total_changes(db); upnp_event_var_change_notify(EContentDirectory); memcpy(&lastupdatetime, &timeofday, sizeof(struct timeval)); } } /* process active HTTP connections */ for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next) { if( (e->socket >= 0) && (e->state <= 2) &&(FD_ISSET(e->socket, &readset)) ) { Process_upnphttp(e); } } /* process incoming HTTP connections */ if(shttpl >= 0 && FD_ISSET(shttpl, &readset)) { int shttp; socklen_t clientnamelen; struct sockaddr_in clientname; clientnamelen = sizeof(struct sockaddr_in); shttp = accept(shttpl, (struct sockaddr *)&clientname, &clientnamelen); if(shttp<0) { DPRINTF(E_ERROR, L_GENERAL, "accept(http): %s\n", strerror(errno)); } else { struct upnphttp * tmp = 0; DPRINTF(E_DEBUG, L_GENERAL, "HTTP connection from %s:%d\n", inet_ntoa(clientname.sin_addr), ntohs(clientname.sin_port) ); /*if (fcntl(shttp, F_SETFL, O_NONBLOCK) < 0) { DPRINTF(E_ERROR, L_GENERAL, "fcntl F_SETFL, O_NONBLOCK"); }*/ /* 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 { DPRINTF(E_ERROR, L_GENERAL, "New_upnphttp() failed\n"); close(shttp); } } } /* delete finished HTTP connections */ for(e = upnphttphead.lh_first; e != NULL; ) { next = e->entries.le_next; if(e->state >= 100) { LIST_REMOVE(e, entries); Delete_upnphttp(e); } e = next; } } shutdown: /* kill the scanner */ if( scanning && scanner_pid ) { kill(scanner_pid, 9); } /* 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); #ifdef TIVO_SUPPORT if (sbeacon >= 0) close(sbeacon); #endif if(SendSSDPGoodbye(snotify, n_lan_addr) < 0) { DPRINTF(E_ERROR, L_GENERAL, "Failed to broadcast good-bye notifications\n"); } for(i=0; i<n_lan_addr; i++) close(snotify[i]); if( inotify_thread ) pthread_join(inotify_thread, NULL); sql_exec(db, "UPDATE SETTINGS set UPDATE_ID = %u", updateID); sqlite3_close(db); struct media_dir_s * media_path = media_dirs; struct media_dir_s * last_path; while( media_path ) { free(media_path->path); last_path = media_path; media_path = media_path->next; free(last_path); } struct album_art_name_s * art_names = album_art_names; struct album_art_name_s * last_name; while( art_names ) { free(art_names->name); last_name = art_names; art_names = art_names->next; free(last_name); } if(unlink(pidfilename) < 0) { DPRINTF(E_ERROR, L_GENERAL, "Failed to remove pidfile %s: %s\n", pidfilename, strerror(errno)); } freeoptions(); exit(EXIT_SUCCESS); }
int add_pinhole(const char * ifname, const char * rem_host, unsigned short rem_port, const char * int_client, unsigned short int_port, int proto, const char * desc, unsigned int timestamp) { int uid; struct pfioc_rule pcr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; #endif if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } memset(&pcr, 0, sizeof(pcr)); strlcpy(pcr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m"); return -1; } else { pcr.pool_ticket = pp.ticket; #else { #endif pcr.rule.direction = PF_IN; pcr.rule.action = PF_PASS; pcr.rule.af = AF_INET6; #ifdef PF_NEWSTYLE pcr.rule.nat.addr.type = PF_ADDR_NONE; pcr.rule.rdr.addr.type = PF_ADDR_NONE; #endif #ifdef USE_IFNAME_IN_RULES if(ifname) strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ); #endif pcr.rule.proto = proto; pcr.rule.quick = 1;/*(GETFLAG(PFNOQUICKRULESMASK))?0:1;*/ pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/ /* see the discussion on the forum : * http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=638 */ pcr.rule.flags = TH_SYN; pcr.rule.flagset = (TH_SYN|TH_ACK); #ifdef PFRULE_HAS_RTABLEID pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */ #endif #ifdef PFRULE_HAS_ONRDOMAIN pcr.rule.onrdomain = -1; /* first appeared in OpenBSD 5.0 */ #endif pcr.rule.keep_state = 1; uid = next_uid; snprintf(pcr.rule.label, PF_RULE_LABEL_SIZE, PINEHOLE_LABEL_FORMAT, uid, timestamp, desc); if(queue) strlcpy(pcr.rule.qname, queue, PF_QNAME_SIZE); if(tag) strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE); if(rem_port) { pcr.rule.src.port_op = PF_OP_EQ; pcr.rule.src.port[0] = htons(rem_port); } if(rem_host && rem_host[0] != '\0' && rem_host[0] != '*') { pcr.rule.src.addr.type = PF_ADDR_ADDRMASK; if(inet_pton(AF_INET6, rem_host, &pcr.rule.src.addr.v.a.addr.v6) != 1) { syslog(LOG_ERR, "inet_pton(%s) failed", rem_host); } memset(&pcr.rule.src.addr.v.a.mask.addr8, 255, 16); } pcr.rule.dst.port_op = PF_OP_EQ; pcr.rule.dst.port[0] = htons(int_port); pcr.rule.dst.addr.type = PF_ADDR_ADDRMASK; if(inet_pton(AF_INET6, int_client, &pcr.rule.dst.addr.v.a.addr.v6) != 1) { syslog(LOG_ERR, "inet_pton(%s) failed", int_client); } memset(&pcr.rule.dst.addr.v.a.mask.addr8, 255, 16); if(ifname) strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ); pcr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); return -1; } else { pcr.action = PF_CHANGE_ADD_TAIL; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m"); return -1; } } } if(++next_uid >= 65535) { next_uid = 1; } return uid; } int delete_pinhole(unsigned short uid) { int i, n; struct pfioc_rule pr; char label_start[PF_RULE_LABEL_SIZE]; char tmp_label[PF_RULE_LABEL_SIZE]; if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } snprintf(label_start, sizeof(label_start), "pinhole-%hu", uid); memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE pr.rule.action = PF_PASS; #endif if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); return -1; } n = pr.nr; for(i=0; i<n; i++) { pr.nr = i; if(ioctl(dev, DIOCGETRULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); return -1; } strlcpy(tmp_label, pr.rule.label, sizeof(tmp_label)); strtok(tmp_label, " "); if(0 == strcmp(tmp_label, label_start)) { pr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); return -1; } pr.action = PF_CHANGE_REMOVE; pr.nr = i; if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m"); return -1; } return 0; } } /* not found */ return -2; }
/* process HTTP or SSDP requests */ int main(int argc, char * * argv) { int i; 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 = NULL; #endif #ifdef ENABLE_NFQUEUE int nfqh = -1; #endif #ifdef USE_IFACEWATCHER int sifacewatcher = -1; #endif int * snotify = NULL; 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; #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 LIST_INIT(&ctllisthead); #endif if( #ifdef ENABLE_NATPMP !GETFLAG(ENABLENATPMPMASK) && #endif !GETFLAG(ENABLEUPNPMASK) ) { syslog(LOG_ERR, "Why did you run me anyway?"); 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)) { /* open socket for HTTP connections. Listen on the 1st LAN address */ shttpl = OpenAndConfHTTPSocket((v.port > 0) ? v.port : 0); if(shttpl < 0) { syslog(LOG_ERR, "Failed to open socket for HTTP. EXITING"); return 1; } if(v.port <= 0) { struct sockaddr_in sockinfo; socklen_t len = sizeof(struct sockaddr_in); if (getsockname(shttpl, (struct sockaddr *)&sockinfo, &len) < 0) { syslog(LOG_ERR, "getsockname(): %m"); return 1; } 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(0); if(sudp < 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) { 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) { syslog(LOG_ERR, "Failed to open sockets for NAT PMP."); } else { syslog(LOG_NOTICE, "Listening for NAT-PMP traffic on port %u", NATPMP_PORT); } #if 0 ScanNATPMPforExpiration(); #endif } #endif /* 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 /* main loop */ while(!quitting) { /* Correct startup_time if it was set with a RTC close to 0 */ 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) { 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, 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 && ((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)) { /*syslog(LOG_DEBUG, "cleaning expired NAT-PMP mappings");*/ if(CleanExpiredNATPMP() < 0) { syslog(LOG_ERR, "CleanExpiredNATPMP() failed"); break; } } 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);*/ timeout.tv_sec = nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec; timeout.tv_usec = 0; } #endif #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) { 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) { 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) { 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++; } } /* 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 #ifdef USE_MINIUPNPDCTL if(sctl >= 0) { 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) { FD_SET(ectl->socket, &readset); max_fd = MAX( max_fd, ectl->socket); } } #endif #ifdef ENABLE_EVENTS upnpevents_selectfds(&readset, &writeset, &max_fd); #endif if(select(max_fd+1, &readset, &writeset, 0, &timeout) < 0) { if(quitting) goto shutdown; 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 */ } #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); write_option_list(ectl->socket); 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); //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); } #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)) { ProcessIncomingNATPMPPacket(snatpmp[i]); } } #endif /* process SSDP packets */ if(sudp >= 0 && FD_ISSET(sudp, &readset)) { /*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) { if(FD_ISSET(e->socket, &readset) || FD_ISSET(e->socket, &writeset)) { Process_upnphttp(e); } } } /* process incoming HTTP connections */ if(shttpl >= 0 && FD_ISSET(shttpl, &readset)) { 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)); syslog(LOG_INFO, "HTTP connection from %s", addr_str); /* 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)) { /* 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; if(e->state >= EToDelete) { LIST_REMOVE(e, entries); Delete_upnphttp(e); } e = next; } } /* end of main loop */ shutdown: /* 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); #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 #ifdef USE_MINIUPNPDCTL if(sctl>=0) { close(sctl); sctl = -1; if(unlink("/var/run/miniupnpd.ctl") < 0) { syslog(LOG_ERR, "unlink() %m"); } } #endif 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"); } #ifndef ENABLE_IPV6 for(i = 0; i < addr_count; i++) #else for(i = 0; i < addr_count * 2; i++) #endif close(snotify[i]); } if(pidfilename && (unlink(pidfilename) < 0)) { syslog(LOG_ERR, "Failed to remove pidfile %s: %m", pidfilename); } /* delete lists */ while(lan_addrs.lh_first != NULL) { lan_addr = lan_addrs.lh_first; LIST_REMOVE(lan_addrs.lh_first, list); free(lan_addr); } #ifdef ENABLE_NATPMP free(snatpmp); #endif free(snotify); closelog(); #ifndef DISABLE_CONFIG_FILE freeoptions(); #endif return 0; }