File minidlna-1.3.0-1.3.1.patch of Package minidlna.17423
diff --git a/Makefile.am b/Makefile.am
index 74859c1..1e33833 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -36,6 +36,10 @@ else
minidlnad_SOURCES += select.c
endif
+if HAVE_INOTIFY
+minidlnad_SOURCES += monitor_inotify.c
+endif
+
if HAVE_VORBISFILE
vorbislibs = -lvorbis -logg
else
diff --git a/NEWS b/NEWS
index 731ffe5..83bfef3 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,11 @@
+1.3.1 - Released 11-Feb-2022
+--------------------------------
+- Fixed a potential crash in SSDP request parsing.
+- Fixed a configure script failure on some platforms.
+- Protect against DNS rebinding attacks.
+- Fix an socket leakage issue on some platforms.
+- Minor bug fixes.
+
1.3.0 - Released 24-Nov-2020
--------------------------------
- Fixed some build warnings when building with musl.
diff --git a/configure.ac b/configure.ac
index cb596b9..92bca0d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -29,6 +29,7 @@ m4_ifdef([AC_USE_SYSTEM_EXTENSIONS], [AC_USE_SYSTEM_EXTENSIONS])
AM_ICONV
AM_GNU_GETTEXT([external])
AM_GNU_GETTEXT_VERSION(0.18)
+AM_GNU_GETTEXT_REQUIRE_VERSION(0.18)
# Checks for programs.
AC_PROG_AWK
@@ -414,7 +415,10 @@ for dir in "" /usr/local $SEARCH_DIR; do
AC_CHECK_LIB([id3tag -lz], [id3_file_open], [LIBID3TAG_LIBS="-lid3tag -lz"], [unset ac_cv_lib_id3tag_id3_file_open; LDFLAGS="$LDFLAGS_SAVE"; continue])
break
done
-test x"$ac_cv_lib_id3tag__lz___id3_file_open" = x"yes" || AC_MSG_ERROR([Could not find libid3tag])
+if test x"$ac_cv_lib_id3tag__lz___id3_file_open" != x"yes" &&
+ test x"$ac_cv_lib_id3tag__lz_id3_file_open" != x"yes"; then
+ AC_MSG_ERROR([Could not find libid3tag])
+fi
AC_SUBST(LIBID3TAG_LIBS)
LDFLAGS_SAVE="$LDFLAGS"
@@ -441,7 +445,8 @@ for dir in "" /usr/local $SEARCH_DIR; do
break
done
if test x"$ac_cv_lib_avformat__lavcodec__lavutil__lz___av_open_input_file" != x"yes" &&
- test x"$ac_cv_lib_avformat__lavcodec__lavutil__lz___avformat_open_input" != x"yes"; then
+ test x"$ac_cv_lib_avformat__lavcodec__lavutil__lz___avformat_open_input" != x"yes" &&
+ test x"$ac_cv_lib_avformat__lavcodec__lavutil__lz_avformat_open_input" != x"yes"; then
AC_MSG_ERROR([Could not find libavformat - part of ffmpeg])
fi
AC_SUBST(LIBAVFORMAT_LIBS)
@@ -490,7 +495,12 @@ AC_CHECK_HEADERS([arpa/inet.h asm/unistd.h endian.h machine/endian.h fcntl.h lib
test x"$ac_cv_header_poll_h" != x"yes" && AC_MSG_ERROR([poll.h not found or not usable])
test x"$ac_cv_header_sys_queue_h" != x"yes" && AC_MSG_ERROR([sys/queue.h not found or not usable])
-AC_CHECK_FUNCS(inotify_init, AC_DEFINE(HAVE_INOTIFY,1,[Whether kernel has inotify support]), [
+AC_CHECK_FUNCS(inotify_init,
+[
+ AC_DEFINE(HAVE_INOTIFY,1,[Whether kernel has inotify support])
+ AM_CONDITIONAL(HAVE_INOTIFY, true)
+],
+[
AC_MSG_CHECKING([for __NR_inotify_init syscall])
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
@@ -506,9 +516,11 @@ AC_CHECK_FUNCS(inotify_init, AC_DEFINE(HAVE_INOTIFY,1,[Whether kernel has inotif
[
AC_MSG_RESULT([yes])
AC_DEFINE(HAVE_INOTIFY,1,[Whether kernel has inotify support])
+ AM_CONDITIONAL(HAVE_INOTIFY, true)
],
[
AC_MSG_RESULT([no])
+ AM_CONDITIONAL(HAVE_INOTIFY, false)
])
])
diff --git a/event.h b/event.h
index c05c61b..6b30a21 100644
--- a/event.h
+++ b/event.h
@@ -42,7 +42,7 @@ typedef int event_module_add_t(struct event *);
typedef int event_module_del_t(struct event *, int flags);
typedef int event_module_init_t(void);
typedef void event_module_fini_t(void);
-typedef int event_module_process_t(u_long);
+typedef int event_module_process_t(struct timeval *);
struct event_module {
event_module_add_t *add;
event_module_del_t *del;
diff --git a/kqueue.c b/kqueue.c
index 67b2c86..35b3f2b 100644
--- a/kqueue.c
+++ b/kqueue.c
@@ -178,27 +178,21 @@ kqueue_set(struct event *ev, short filter, u_short flags, u_int fflags)
}
static int
-kqueue_process(u_long timer)
+kqueue_process(struct timeval *tv)
{
struct event *ev;
int events, n, i;
- struct timespec ts, *tp;
+ struct timespec ts;
n = (int) nchanges;
nchanges = 0;
- if (timer == 0) {
- tp = NULL;
- } else {
- ts.tv_sec = timer / 1000;
- ts.tv_nsec = (timer % 1000) * 1000000;
- tp = &ts;
- }
+ TIMEVAL_TO_TIMESPEC(tv, &ts);
- DPRINTF(E_DEBUG, L_GENERAL, "kevent timer: %lu, changes: %d\n",
- timer, n);
+ DPRINTF(E_DEBUG, L_GENERAL, "kevent timer: %lu.%06lu, changes: %d\n",
+ ts.tv_sec, ts.tv_nsec, n);
- events = kevent(kq, change_list, n, event_list, MAXEVENTS, tp);
+ events = kevent(kq, change_list, n, event_list, MAXEVENTS, &ts);
if (events == -1) {
if (errno == EINTR)
@@ -208,12 +202,6 @@ kqueue_process(u_long timer)
DPRINTF(E_DEBUG, L_GENERAL, "kevent events: %d\n", events);
- if (events == 0) {
- if (timer != 0)
- return (0);
- DPRINTF(E_FATAL, L_GENERAL, "kevent() returned no events. EXITING\n");
- }
-
for (i = 0; i < events; i++) {
if (event_list[i].flags & EV_ERROR) {
DPRINTF(E_ERROR, L_GENERAL,
diff --git a/minidlna.c b/minidlna.c
index b2769ae..999adee 100644
--- a/minidlna.c
+++ b/minidlna.c
@@ -64,7 +64,6 @@
#include <time.h>
#include <signal.h>
#include <errno.h>
-#include <pthread.h>
#include <limits.h>
#include <libgen.h>
#include <pwd.h>
@@ -1088,6 +1087,19 @@ init(int argc, char **argv)
return 0;
}
+#ifdef HAVE_WATCH
+void
+start_monitor()
+{
+
+ if (!GETFLAG(INOTIFY_MASK))
+ return;
+
+ lav_register_all();
+ monitor_start();
+}
+#endif
+
/* === main === */
/* process HTTP or SSDP requests */
int
@@ -1100,10 +1112,8 @@ main(int argc, char **argv)
struct upnphttp * next;
struct timeval tv, timeofday, lastnotifytime = {0, 0};
time_t lastupdatetime = 0, lastdbtime = 0;
- u_long timeout; /* in milliseconds */
int last_changecnt = 0;
pid_t scanner_pid = 0;
- pthread_t inotify_thread = 0;
struct event ssdpev, httpev, monev;
#ifdef TIVO_SUPPORT
uint8_t beacon_interval = 5;
@@ -1138,23 +1148,11 @@ main(int argc, char **argv)
}
check_db(db, ret, &scanner_pid);
lastdbtime = _get_dbtime();
-#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 /* HAVE_INOTIFY */
-#ifdef HAVE_KQUEUE
- if (!GETFLAG(SCANNING_MASK)) {
- lav_register_all();
- kqueue_monitor_start();
- }
-#endif /* HAVE_KQUEUE */
+#ifdef HAVE_WATCH
+ if (!GETFLAG(SCANNING_MASK))
+ start_monitor();
+#endif
smonitor = OpenAndConfMonitorSocket();
if (smonitor > 0)
@@ -1185,6 +1183,9 @@ main(int argc, char **argv)
httpev = (struct event ){ .fd = shttpl, .rdwr = EVENT_READ, .process = ProcessListen };
event_module.add(&httpev);
+ if (gettimeofday(&timeofday, 0) < 0)
+ DPRINTF(E_FATAL, L_GENERAL, "gettimeofday(): %s\n", strerror(errno));
+
#ifdef TIVO_SUPPORT
if (GETFLAG(TIVO_MASK))
{
@@ -1209,18 +1210,17 @@ main(int argc, char **argv)
tivo_bcast.sin_family = AF_INET;
tivo_bcast.sin_addr.s_addr = htonl(getBcastAddress());
tivo_bcast.sin_port = htons(2190);
+ lastbeacontime = timeofday;
}
}
#endif
- reload_ifaces(0);
- lastnotifytime.tv_sec = time(NULL) + runtime_vars.notify_interval;
+ reload_ifaces(0); /* sends SSDP notifies */
+ lastnotifytime = timeofday;
/* main loop */
while (!quitting)
{
- if (gettimeofday(&timeofday, 0) < 0)
- DPRINTF(E_FATAL, L_GENERAL, "gettimeofday(): %s\n", strerror(errno));
/* Check if we need to send SSDP NOTIFY messages and do it if
* needed */
tv = lastnotifytime;
@@ -1234,58 +1234,62 @@ main(int argc, char **argv)
runtime_vars.port, runtime_vars.notify_interval);
}
lastnotifytime = timeofday;
- timeout = runtime_vars.notify_interval * 1000;
+ tv.tv_sec = runtime_vars.notify_interval;
+ tv.tv_usec = 0;
}
else
{
timevalsub(&tv, &timeofday);
- timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
#ifdef TIVO_SUPPORT
if (sbeacon >= 0)
{
- u_long beacontimeout;
+ struct timeval beacontv;
- tv = lastbeacontime;
- tv.tv_sec += beacon_interval;
- if (timevalcmp(&timeofday, &tv, >=))
+ beacontv = lastbeacontime;
+ beacontv.tv_sec += beacon_interval;
+ if (timevalcmp(&timeofday, &beacontv, >=))
{
sendBeaconMessage(sbeacon, &tivo_bcast, sizeof(struct sockaddr_in), 1);
lastbeacontime = timeofday;
- beacontimeout = beacon_interval * 1000;
- if (timeout > beacon_interval * 1000)
- timeout = beacon_interval * 1000;
/* 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;
+ beacontv.tv_sec = beacon_interval;
+ beacontv.tv_usec = 0;
}
else
{
- timevalsub(&tv, &timeofday);
- beacontimeout = tv.tv_sec * 1000 +
- tv.tv_usec / 1000;
+ timevalsub(&beacontv, &timeofday);
}
- if (timeout > beacontimeout)
- timeout = beacontimeout;
+ if (timevalcmp(&tv, &beacontv, >))
+ tv = beacontv;
}
#endif
- if (GETFLAG(SCANNING_MASK) && kill(scanner_pid, 0) != 0) {
- CLEARFLAG(SCANNING_MASK);
- if (_get_dbtime() != lastdbtime)
- updateID++;
-#ifdef HAVE_KQUEUE
- lav_register_all();
- kqueue_monitor_start();
-#endif /* HAVE_KQUEUE */
+ if (GETFLAG(SCANNING_MASK)) {
+ if (kill(scanner_pid, 0) != 0) {
+ DPRINTF(E_INFO, L_GENERAL, "Scanner exited\n");
+ CLEARFLAG(SCANNING_MASK);
+ if (_get_dbtime() != lastdbtime)
+ updateID++;
+#ifdef HAVE_WATCH
+ start_monitor();
+#endif
+ } else
+ /* Keep checking for the scanner every sec. */
+ tv.tv_sec = 1;
}
- event_module.process(timeout);
+ event_module.process(&tv);
if (quitting)
goto shutdown;
+ if (gettimeofday(&timeofday, 0) < 0)
+ DPRINTF(E_FATAL, L_GENERAL, "gettimeofday(): %s\n", strerror(errno));
+
upnpevents_gc();
/* increment SystemUpdateID if the content database has changed,
@@ -1351,11 +1355,9 @@ shutdown:
close(lan_addr[i].snotify);
}
- if (inotify_thread)
- {
- pthread_kill(inotify_thread, SIGCHLD);
- pthread_join(inotify_thread, NULL);
- }
+#ifdef HAVE_WATCH
+ monitor_stop();
+#endif
/* kill other child processes */
process_reap_children();
diff --git a/minissdp.c b/minissdp.c
index 0e9c68a..fab0e92 100644
--- a/minissdp.c
+++ b/minissdp.c
@@ -552,27 +552,27 @@ ProcessSSDPRequest(struct event *ev)
if (strncasecmp(bufr+i, "SERVER:", 7) == 0)
{
srv = bufr+i+7;
- while (*srv == ' ' || *srv == '\t')
+ while (*srv && (*srv == ' ' || *srv == '\t'))
srv++;
}
else if (strncasecmp(bufr+i, "LOCATION:", 9) == 0)
{
loc = bufr+i+9;
- while (*loc == ' ' || *loc == '\t')
+ while (*loc && (*loc == ' ' || *loc == '\t'))
loc++;
- while (loc[loc_len]!='\r' && loc[loc_len]!='\n')
+ while (loc[loc_len] && (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')
+ while (*nts && (*nts == ' ' || *nts == '\t'))
nts++;
}
else if (strncasecmp(bufr+i, "NT:", 3) == 0)
{
nt = bufr+i+3;
- while(*nt == ' ' || *nt == '\t')
+ while(*nt && (*nt == ' ' || *nt == '\t'))
nt++;
}
}
diff --git a/monitor.c b/monitor.c
index 9e56bc7..edbe308 100644
--- a/monitor.c
+++ b/monitor.c
@@ -30,16 +30,6 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
-#ifdef HAVE_INOTIFY
-#include <sys/resource.h>
-#include <poll.h>
-#ifdef HAVE_SYS_INOTIFY_H
-#include <sys/inotify.h>
-#else
-#include "linux/inotify.h"
-#include "linux/inotify-syscalls.h"
-#endif
-#endif
#include "libav.h"
#include "upnpglobalvars.h"
@@ -52,208 +42,7 @@
#include "playlist.h"
#include "log.h"
-static time_t next_pl_fill = 0;
-
-#ifdef HAVE_INOTIFY
-#define EVENT_SIZE ( sizeof (struct inotify_event) )
-#define BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
-#define DESIRED_WATCH_LIMIT 65536
-
-#define PATH_BUF_SIZE PATH_MAX
-
-struct watch
-{
- int wd; /* watch descriptor */
- char *path; /* watched path */
- struct watch *next;
-};
-
-static struct watch *watches;
-static struct watch *lastwatch = NULL;
-
-static char *
-get_path_from_wd(int wd)
-{
- struct watch *w = watches;
-
- while( w != NULL )
- {
- if( w->wd == wd )
- return w->path;
- w = w->next;
- }
-
- return NULL;
-}
-
-static unsigned int
-next_highest(unsigned int num)
-{
- num |= num >> 1;
- num |= num >> 2;
- num |= num >> 4;
- num |= num >> 8;
- num |= num >> 16;
- return ++num;
-}
-
-static void
-raise_watch_limit(unsigned int limit)
-{
- FILE *max_watches = fopen("/proc/sys/fs/inotify/max_user_watches", "r+");
- if (!max_watches)
- return;
- if (!limit)
- {
- if (fscanf(max_watches, "%10u", &limit) < 1)
- limit = 8192;
- rewind(max_watches);
- }
- fprintf(max_watches, "%u", next_highest(limit));
- fclose(max_watches);
-}
-
-int
-add_watch(int fd, const char * path)
-{
- struct watch *nw;
- int wd;
-
- wd = inotify_add_watch(fd, path, IN_CREATE|IN_CLOSE_WRITE|IN_DELETE|IN_MOVE);
- if( wd < 0 && errno == ENOSPC)
- {
- raise_watch_limit(0);
- wd = inotify_add_watch(fd, path, IN_CREATE|IN_CLOSE_WRITE|IN_DELETE|IN_MOVE);
- }
- if( wd < 0 )
- {
- DPRINTF(E_ERROR, L_INOTIFY, "inotify_add_watch(%s) [%s]\n", path, strerror(errno));
- return (errno);
- }
-
- nw = malloc(sizeof(struct watch));
- if( nw == NULL )
- {
- DPRINTF(E_ERROR, L_INOTIFY, "malloc() error\n");
- return (ENOMEM);
- }
- nw->wd = wd;
- nw->next = NULL;
- nw->path = strdup(path);
-
- if( watches == NULL )
- {
- watches = nw;
- }
-
- if( lastwatch != NULL )
- {
- lastwatch->next = nw;
- }
- lastwatch = nw;
-
- DPRINTF(E_INFO, L_INOTIFY, "Added watch to %s [%d]\n", path, wd);
- return (0);
-}
-
-static int
-remove_watch(int fd, const char * path)
-{
- struct watch *w;
-
- for( w = watches; w; w = w->next )
- {
- if( strcmp(path, w->path) == 0 )
- return(inotify_rm_watch(fd, w->wd));
- }
-
- return 1;
-}
-
-static int
-inotify_create_watches(int fd)
-{
- FILE * max_watches;
- unsigned int num_watches = 0, watch_limit;
- char **result;
- int i, rows = 0;
- struct media_dir_s * media_path;
-
- for( media_path = media_dirs; media_path != NULL; media_path = media_path->next )
- {
- DPRINTF(E_DEBUG, L_INOTIFY, "Add watch to %s\n", media_path->path);
- add_watch(fd, media_path->path);
- num_watches++;
- }
- sql_get_table(db, "SELECT PATH from DETAILS where MIME is NULL and PATH is not NULL", &result, &rows, NULL);
- for( i=1; i <= rows; i++ )
- {
- DPRINTF(E_DEBUG, L_INOTIFY, "Add watch to %s\n", result[i]);
- add_watch(fd, result[i]);
- num_watches++;
- }
- sqlite3_free_table(result);
-
- max_watches = fopen("/proc/sys/fs/inotify/max_user_watches", "r");
- if( max_watches )
- {
- if( fscanf(max_watches, "%10u", &watch_limit) < 1 )
- watch_limit = 8192;
- fclose(max_watches);
- if( (watch_limit < DESIRED_WATCH_LIMIT) || (watch_limit < (num_watches*4/3)) )
- {
- if (access("/proc/sys/fs/inotify/max_user_watches", W_OK) == 0)
- {
- if( DESIRED_WATCH_LIMIT >= (num_watches*3/4) )
- {
- raise_watch_limit(8191U);
- }
- else if( next_highest(num_watches) >= (num_watches*3/4) )
- {
- raise_watch_limit(num_watches);
- }
- else
- {
- raise_watch_limit(next_highest(num_watches));
- }
- }
- else
- {
- DPRINTF(E_WARN, L_INOTIFY, "WARNING: Inotify max_user_watches [%u] is low or close to the number of used watches [%u] "
- "and I do not have permission to increase this limit. Please do so manually by "
- "writing a higher value into /proc/sys/fs/inotify/max_user_watches.\n", watch_limit, num_watches);
- }
- }
- }
- else
- {
- DPRINTF(E_WARN, L_INOTIFY, "WARNING: Could not read inotify max_user_watches! "
- "Hopefully it is enough to cover %u current directories plus any new ones added.\n", num_watches);
- }
-
- return rows;
-}
-
-static int
-inotify_remove_watches(int fd)
-{
- struct watch *w = watches;
- struct watch *last_w;
- int rm_watches = 0;
-
- while( w )
- {
- last_w = w;
- inotify_rm_watch(fd, w->wd);
- free(w->path);
- rm_watches++;
- w = w->next;
- free(last_w);
- }
-
- return rm_watches;
-}
-#endif
+time_t next_pl_fill = 0;
int
monitor_remove_file(const char * path)
@@ -535,7 +324,7 @@ monitor_insert_directory(int fd, char *name, const char * path)
#ifdef HAVE_WATCH
if( fd > 0 )
- add_watch(fd, path);
+ monitor_add_watch(fd, path);
#endif
dir_types = valid_media_types(path);
@@ -586,12 +375,12 @@ monitor_remove_directory(int fd, const char * path)
/* Invalidate the scanner cache so we don't insert files into non-existent containers */
valid_cache = 0;
- #ifdef HAVE_INOTIFY
+#ifdef HAVE_WATCH
if( fd > 0 )
{
- remove_watch(fd, path);
+ monitor_remove_watch(fd, path);
}
- #endif
+#endif
sql = sqlite3_mprintf("SELECT ID from DETAILS where (PATH > '%q/' and PATH <= '%q/%c')"
" or PATH = '%q'", path, path, 0xFF, path);
if( (sql_get_table(db, sql, &result, &rows, NULL) == SQLITE_OK) )
@@ -614,137 +403,3 @@ monitor_remove_directory(int fd, const char * path)
return ret;
}
-
-#ifdef HAVE_INOTIFY
-void *
-start_inotify(void)
-{
- struct pollfd pollfds[1];
- char buffer[BUF_LEN];
- char path_buf[PATH_MAX];
- int length, i = 0;
- char * esc_name = NULL;
- struct stat st;
- sigset_t set;
-
- sigfillset(&set);
- sigdelset(&set, SIGCHLD);
- pthread_sigmask(SIG_BLOCK, &set, NULL);
-
- pollfds[0].fd = inotify_init();
- pollfds[0].events = POLLIN;
-
- if ( pollfds[0].fd < 0 )
- DPRINTF(E_ERROR, L_INOTIFY, "inotify_init() failed!\n");
-
- while( GETFLAG(SCANNING_MASK) )
- {
- if( quitting )
- goto quitting;
- sleep(1);
- }
- inotify_create_watches(pollfds[0].fd);
- if (setpriority(PRIO_PROCESS, 0, 19) == -1)
- DPRINTF(E_WARN, L_INOTIFY, "Failed to reduce inotify thread priority\n");
- sqlite3_release_memory(1<<31);
- lav_register_all();
-
- while( !quitting )
- {
- int timeout = -1;
- if (next_pl_fill)
- {
- time_t diff = next_pl_fill - time(NULL);
- if (diff < 0)
- timeout = 0;
- else
- timeout = diff * 1000;
- }
- length = poll(pollfds, 1, timeout);
- if( !length )
- {
- if( next_pl_fill && (time(NULL) >= next_pl_fill) )
- {
- fill_playlists();
- next_pl_fill = 0;
- }
- continue;
- }
- else if( length < 0 )
- {
- if( (errno == EINTR) || (errno == EAGAIN) )
- continue;
- else
- DPRINTF(E_ERROR, L_INOTIFY, "read failed!\n");
- }
- else
- {
- length = read(pollfds[0].fd, buffer, BUF_LEN);
- buffer[BUF_LEN-1] = '\0';
- }
-
- i = 0;
- while( !quitting && i < length )
- {
- struct inotify_event * event = (struct inotify_event *) &buffer[i];
- if( event->len )
- {
- if( *(event->name) == '.' )
- {
- i += EVENT_SIZE + event->len;
- continue;
- }
- esc_name = modifyString(strdup(event->name), "&", "&amp;", 0);
- snprintf(path_buf, sizeof(path_buf), "%s/%s", get_path_from_wd(event->wd), event->name);
- if ( event->mask & IN_ISDIR && (event->mask & (IN_CREATE|IN_MOVED_TO)) )
- {
- DPRINTF(E_DEBUG, L_INOTIFY, "The directory %s was %s.\n",
- path_buf, (event->mask & IN_MOVED_TO ? "moved here" : "created"));
- monitor_insert_directory(pollfds[0].fd, esc_name, path_buf);
- }
- else if ( (event->mask & (IN_CLOSE_WRITE|IN_MOVED_TO|IN_CREATE)) &&
- (lstat(path_buf, &st) == 0) )
- {
- if( (event->mask & (IN_MOVED_TO|IN_CREATE)) && (S_ISLNK(st.st_mode) || st.st_nlink > 1) )
- {
- DPRINTF(E_DEBUG, L_INOTIFY, "The %s link %s was %s.\n",
- (S_ISLNK(st.st_mode) ? "symbolic" : "hard"),
- path_buf, (event->mask & IN_MOVED_TO ? "moved here" : "created"));
- if( stat(path_buf, &st) == 0 && S_ISDIR(st.st_mode) )
- monitor_insert_directory(pollfds[0].fd, esc_name, path_buf);
- else
- monitor_insert_file(esc_name, path_buf);
- }
- else if( event->mask & (IN_CLOSE_WRITE|IN_MOVED_TO) && st.st_size > 0 )
- {
- if( (event->mask & IN_MOVED_TO) ||
- (sql_get_int_field(db, "SELECT TIMESTAMP from DETAILS where PATH = '%q'", path_buf) != st.st_mtime) )
- {
- DPRINTF(E_DEBUG, L_INOTIFY, "The file %s was %s.\n",
- path_buf, (event->mask & IN_MOVED_TO ? "moved here" : "changed"));
- monitor_insert_file(esc_name, path_buf);
- }
- }
- }
- else if ( event->mask & (IN_DELETE|IN_MOVED_FROM) )
- {
- DPRINTF(E_DEBUG, L_INOTIFY, "The %s %s was %s.\n",
- (event->mask & IN_ISDIR ? "directory" : "file"),
- path_buf, (event->mask & IN_MOVED_FROM ? "moved away" : "deleted"));
- if ( event->mask & IN_ISDIR )
- monitor_remove_directory(pollfds[0].fd, path_buf);
- else
- monitor_remove_file(path_buf);
- }
- free(esc_name);
- }
- i += EVENT_SIZE + event->len;
- }
- }
- inotify_remove_watches(pollfds[0].fd);
-quitting:
- close(pollfds[0].fd);
-
- return 0;
-}
-#endif
diff --git a/monitor.h b/monitor.h
index c5e7b99..1040df5 100644
--- a/monitor.h
+++ b/monitor.h
@@ -5,14 +5,8 @@ int monitor_remove_directory(int fd, const char * path);
#if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
#define HAVE_WATCH 1
-int add_watch(int, const char *);
-#endif
-
-#ifdef HAVE_INOTIFY
-void *
-start_inotify();
-#endif
-
-#ifdef HAVE_KQUEUE
-void kqueue_monitor_start();
+int monitor_add_watch(int, const char *);
+int monitor_remove_watch(int, const char *);
+void monitor_start();
+void monitor_stop();
#endif
diff --git a/monitor_inotify.c b/monitor_inotify.c
new file mode 100644
index 0000000..6e4276c
--- /dev/null
+++ b/monitor_inotify.c
@@ -0,0 +1,404 @@
+/* MiniDLNA media server
+ * Copyright (C) 2008-2010 Justin Maggard
+ *
+ * This file is part of MiniDLNA.
+ *
+ * MiniDLNA is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * MiniDLNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#ifdef HAVE_INOTIFY
+#include <sys/resource.h>
+#include <poll.h>
+#ifdef HAVE_SYS_INOTIFY_H
+#include <sys/inotify.h>
+#else
+#include "linux/inotify.h"
+#include "linux/inotify-syscalls.h"
+#endif
+#endif
+#include "libav.h"
+
+#include "upnpglobalvars.h"
+#include "monitor.h"
+#include "utils.h"
+#include "sql.h"
+#include "scanner.h"
+#include "metadata.h"
+#include "albumart.h"
+#include "playlist.h"
+#include "log.h"
+
+extern time_t next_pl_fill;
+
+#define EVENT_SIZE ( sizeof (struct inotify_event) )
+#define BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
+#define DESIRED_WATCH_LIMIT 65536
+
+#define PATH_BUF_SIZE PATH_MAX
+
+struct watch
+{
+ int wd; /* watch descriptor */
+ char *path; /* watched path */
+ struct watch *next;
+};
+
+static struct watch *watches;
+static struct watch *lastwatch = NULL;
+static pthread_t thread_id;
+
+static char *
+get_path_from_wd(int wd)
+{
+ struct watch *w = watches;
+
+ while( w != NULL )
+ {
+ if( w->wd == wd )
+ return w->path;
+ w = w->next;
+ }
+
+ return NULL;
+}
+
+static unsigned int
+next_highest(unsigned int num)
+{
+ num |= num >> 1;
+ num |= num >> 2;
+ num |= num >> 4;
+ num |= num >> 8;
+ num |= num >> 16;
+ return ++num;
+}
+
+static void
+raise_watch_limit(unsigned int limit)
+{
+ FILE *max_watches = fopen("/proc/sys/fs/inotify/max_user_watches", "r+");
+ if (!max_watches)
+ return;
+ if (!limit)
+ {
+ if (fscanf(max_watches, "%u", &limit) < 1)
+ limit = 8192;
+ rewind(max_watches);
+ }
+ fprintf(max_watches, "%u", next_highest(limit));
+ fclose(max_watches);
+}
+
+int
+monitor_add_watch(int fd, const char * path)
+{
+ struct watch *nw;
+ int wd;
+
+ wd = inotify_add_watch(fd, path, IN_CREATE|IN_CLOSE_WRITE|IN_DELETE|IN_MOVE);
+ if( wd < 0 && errno == ENOSPC)
+ {
+ raise_watch_limit(0);
+ wd = inotify_add_watch(fd, path, IN_CREATE|IN_CLOSE_WRITE|IN_DELETE|IN_MOVE);
+ }
+ if( wd < 0 )
+ {
+ DPRINTF(E_ERROR, L_INOTIFY, "inotify_add_watch(%s) [%s]\n", path, strerror(errno));
+ return (errno);
+ }
+
+ nw = malloc(sizeof(struct watch));
+ if( nw == NULL )
+ {
+ DPRINTF(E_ERROR, L_INOTIFY, "malloc() error\n");
+ return (ENOMEM);
+ }
+ nw->wd = wd;
+ nw->next = NULL;
+ nw->path = strdup(path);
+
+ if( watches == NULL )
+ {
+ watches = nw;
+ }
+
+ if( lastwatch != NULL )
+ {
+ lastwatch->next = nw;
+ }
+ lastwatch = nw;
+
+ DPRINTF(E_INFO, L_INOTIFY, "Added watch to %s [%d]\n", path, wd);
+ return (0);
+}
+
+int
+monitor_remove_watch(int fd, const char * path)
+{
+ struct watch *w;
+
+ for( w = watches; w; w = w->next )
+ {
+ if( strcmp(path, w->path) == 0 )
+ return(inotify_rm_watch(fd, w->wd));
+ }
+
+ return 1;
+}
+
+static int
+inotify_create_watches(int fd)
+{
+ FILE * max_watches;
+ unsigned int num_watches = 0, watch_limit;
+ char **result;
+ int i, rows = 0;
+ struct media_dir_s * media_path;
+
+ for( media_path = media_dirs; media_path != NULL; media_path = media_path->next )
+ {
+ DPRINTF(E_DEBUG, L_INOTIFY, "Add watch to %s\n", media_path->path);
+ monitor_add_watch(fd, media_path->path);
+ num_watches++;
+ }
+ sql_get_table(db, "SELECT PATH from DETAILS where MIME is NULL and PATH is not NULL", &result, &rows, NULL);
+ for( i=1; i <= rows; i++ )
+ {
+ DPRINTF(E_DEBUG, L_INOTIFY, "Add watch to %s\n", result[i]);
+ monitor_add_watch(fd, result[i]);
+ num_watches++;
+ }
+ sqlite3_free_table(result);
+
+ max_watches = fopen("/proc/sys/fs/inotify/max_user_watches", "r");
+ if( max_watches )
+ {
+ if( fscanf(max_watches, "%10u", &watch_limit) < 1 )
+ watch_limit = 8192;
+ fclose(max_watches);
+ if( (watch_limit < DESIRED_WATCH_LIMIT) || (watch_limit < (num_watches*4/3)) )
+ {
+ if (access("/proc/sys/fs/inotify/max_user_watches", W_OK) == 0)
+ {
+ if( DESIRED_WATCH_LIMIT >= (num_watches*3/4) )
+ {
+ raise_watch_limit(8191U);
+ }
+ else if( next_highest(num_watches) >= (num_watches*3/4) )
+ {
+ raise_watch_limit(num_watches);
+ }
+ else
+ {
+ raise_watch_limit(next_highest(num_watches));
+ }
+ }
+ else
+ {
+ DPRINTF(E_WARN, L_INOTIFY, "WARNING: Inotify max_user_watches [%u] is low or close to the number of used watches [%u] "
+ "and I do not have permission to increase this limit. Please do so manually by "
+ "writing a higher value into /proc/sys/fs/inotify/max_user_watches.\n", watch_limit, num_watches);
+ }
+ }
+ }
+ else
+ {
+ DPRINTF(E_WARN, L_INOTIFY, "WARNING: Could not read inotify max_user_watches! "
+ "Hopefully it is enough to cover %u current directories plus any new ones added.\n", num_watches);
+ }
+
+ return rows;
+}
+
+static int
+inotify_remove_watches(int fd)
+{
+ struct watch *w = watches;
+ struct watch *last_w;
+ int rm_watches = 0;
+
+ while( w )
+ {
+ last_w = w;
+ inotify_rm_watch(fd, w->wd);
+ free(w->path);
+ rm_watches++;
+ w = w->next;
+ free(last_w);
+ }
+
+ return rm_watches;
+}
+
+static void *
+inotify_thread(void *arg)
+{
+ struct pollfd pollfds[1];
+ char buffer[BUF_LEN];
+ char path_buf[PATH_MAX];
+ int length, i = 0;
+ char * esc_name = NULL;
+ struct stat st;
+ sigset_t set;
+
+ sigfillset(&set);
+ sigdelset(&set, SIGCHLD);
+ pthread_sigmask(SIG_BLOCK, &set, NULL);
+
+ pollfds[0].fd = inotify_init();
+ pollfds[0].events = POLLIN;
+
+ if ( pollfds[0].fd < 0 )
+ DPRINTF(E_ERROR, L_INOTIFY, "inotify_init() failed!\n");
+
+ inotify_create_watches(pollfds[0].fd);
+ if (setpriority(PRIO_PROCESS, 0, 19) == -1)
+ DPRINTF(E_WARN, L_INOTIFY, "Failed to reduce inotify thread priority\n");
+ sqlite3_release_memory(1<<31);
+
+ while( !quitting )
+ {
+ int timeout = -1;
+ if (next_pl_fill)
+ {
+ time_t diff = next_pl_fill - time(NULL);
+ if (diff < 0)
+ timeout = 0;
+ else
+ timeout = diff * 1000;
+ }
+ length = poll(pollfds, 1, timeout);
+ if( !length )
+ {
+ if( next_pl_fill && (time(NULL) >= next_pl_fill) )
+ {
+ fill_playlists();
+ next_pl_fill = 0;
+ }
+ continue;
+ }
+ else if( length < 0 )
+ {
+ if( (errno == EINTR) || (errno == EAGAIN) )
+ continue;
+ else
+ DPRINTF(E_ERROR, L_INOTIFY, "read failed!\n");
+ }
+ else
+ {
+ length = read(pollfds[0].fd, buffer, BUF_LEN);
+ buffer[BUF_LEN-1] = '\0';
+ }
+
+ i = 0;
+ while( !quitting && i < length )
+ {
+ struct inotify_event * event = (struct inotify_event *) &buffer[i];
+ if( event->len )
+ {
+ if( *(event->name) == '.' )
+ {
+ i += EVENT_SIZE + event->len;
+ continue;
+ }
+ esc_name = modifyString(strdup(event->name), "&", "&amp;", 0);
+ snprintf(path_buf, sizeof(path_buf), "%s/%s", get_path_from_wd(event->wd), event->name);
+ if ( event->mask & IN_ISDIR && (event->mask & (IN_CREATE|IN_MOVED_TO)) )
+ {
+ DPRINTF(E_DEBUG, L_INOTIFY, "The directory %s was %s.\n",
+ path_buf, (event->mask & IN_MOVED_TO ? "moved here" : "created"));
+ monitor_insert_directory(pollfds[0].fd, esc_name, path_buf);
+ }
+ else if ( (event->mask & (IN_CLOSE_WRITE|IN_MOVED_TO|IN_CREATE)) &&
+ (lstat(path_buf, &st) == 0) )
+ {
+ if( (event->mask & (IN_MOVED_TO|IN_CREATE)) && (S_ISLNK(st.st_mode) || st.st_nlink > 1) )
+ {
+ DPRINTF(E_DEBUG, L_INOTIFY, "The %s link %s was %s.\n",
+ (S_ISLNK(st.st_mode) ? "symbolic" : "hard"),
+ path_buf, (event->mask & IN_MOVED_TO ? "moved here" : "created"));
+ if( stat(path_buf, &st) == 0 && S_ISDIR(st.st_mode) )
+ monitor_insert_directory(pollfds[0].fd, esc_name, path_buf);
+ else
+ monitor_insert_file(esc_name, path_buf);
+ }
+ else if( event->mask & (IN_CLOSE_WRITE|IN_MOVED_TO) && st.st_size > 0 )
+ {
+ if( (event->mask & IN_MOVED_TO) ||
+ (sql_get_int_field(db, "SELECT TIMESTAMP from DETAILS where PATH = '%q'", path_buf) != st.st_mtime) )
+ {
+ DPRINTF(E_DEBUG, L_INOTIFY, "The file %s was %s.\n",
+ path_buf, (event->mask & IN_MOVED_TO ? "moved here" : "changed"));
+ monitor_insert_file(esc_name, path_buf);
+ }
+ }
+ }
+ else if ( event->mask & (IN_DELETE|IN_MOVED_FROM) )
+ {
+ DPRINTF(E_DEBUG, L_INOTIFY, "The %s %s was %s.\n",
+ (event->mask & IN_ISDIR ? "directory" : "file"),
+ path_buf, (event->mask & IN_MOVED_FROM ? "moved away" : "deleted"));
+ if ( event->mask & IN_ISDIR )
+ monitor_remove_directory(pollfds[0].fd, path_buf);
+ else
+ monitor_remove_file(path_buf);
+ }
+ free(esc_name);
+ }
+ i += EVENT_SIZE + event->len;
+ }
+ }
+ inotify_remove_watches(pollfds[0].fd);
+ close(pollfds[0].fd);
+
+ return 0;
+}
+
+void
+monitor_start(void)
+{
+
+ if (!sqlite3_threadsafe() || sqlite3_libversion_number() < 3005001) {
+ DPRINTF(E_ERROR, L_GENERAL, "SQLite library is not threadsafe!"
+ "Inotify will be disabled.\n");
+ return;
+ }
+ if (pthread_create(&thread_id, NULL, inotify_thread, NULL) != 0)
+ DPRINTF(E_FATAL, L_GENERAL, "pthread_create() failed [%s]\n",
+ strerror(errno));
+}
+
+void
+monitor_stop(void)
+{
+
+ if (thread_id != 0) {
+ pthread_kill(thread_id, SIGCHLD);
+ pthread_join(thread_id, NULL);
+ }
+}
diff --git a/monitor_kqueue.c b/monitor_kqueue.c
index 34026d5..b16e40f 100644
--- a/monitor_kqueue.c
+++ b/monitor_kqueue.c
@@ -214,7 +214,7 @@ err1:
}
int
-add_watch(int fd __unused, const char *path)
+monitor_add_watch(int fd __unused, const char *path)
{
struct watch *wt;
struct event *ev;
@@ -251,14 +251,20 @@ add_watch(int fd __unused, const char *path)
return (0);
}
+int
+monitor_remove_watch(int fd __unused, const char *path __unused)
+{
+
+ return (0);
+}
+
/*
- * XXXGL: this function has too much copypaste of inotify_create_watches().
- * We need to split out inotify stuff from monitor.c into monitor_inotify.c,
- * compile the latter on Linux and this file on FreeBSD, and keep monitor.c
- * itself platform independent.
+ * XXXGL: this function has some copypaste with inotify_create_watches().
+ * We need to push more code to platform independent start_monitor()
+ * in minidlna.c.
*/
void
-kqueue_monitor_start()
+monitor_start()
{
struct media_dir_s *media_path;
char **result;
@@ -267,9 +273,14 @@ kqueue_monitor_start()
DPRINTF(E_DEBUG, L_INOTIFY, "kqueue monitoring starting\n");
for (media_path = media_dirs; media_path != NULL;
media_path = media_path->next)
- add_watch(0, media_path->path);
+ monitor_add_watch(0, media_path->path);
sql_get_table(db, "SELECT PATH from DETAILS where MIME is NULL and PATH is not NULL", &result, &rows, NULL);
for (i = 1; i <= rows; i++ )
- add_watch(0, result[i]);
+ monitor_add_watch(0, result[i]);
sqlite3_free_table(result);
}
+
+void
+monitor_stop()
+{
+}
diff --git a/process.c b/process.c
index abb777b..f55826a 100644
--- a/process.c
+++ b/process.c
@@ -123,7 +123,8 @@ process_handle_child_termination(int signal)
else
break;
}
- number_of_children--;
+ if (number_of_children)
+ number_of_children--;
remove_process_info(pid);
}
}
diff --git a/select.c b/select.c
index dce7311..e287760 100644
--- a/select.c
+++ b/select.c
@@ -142,9 +142,8 @@ select_del(struct event *ev, int flags)
}
static int
-select_process(u_long msec)
+select_process(struct timeval *tv)
{
- struct timeval tv, *tp;
struct event *ev;
int ready, i;
@@ -155,14 +154,10 @@ select_process(u_long msec)
max_fd = events[i]->fd;
}
- tv.tv_sec = (long) (msec / 1000);
- tv.tv_usec = (long) ((msec % 1000) * 1000);
- tp = &tv;
-
work_read_fd_set = master_read_fd_set;
work_write_fd_set = master_write_fd_set;
- ready = select(max_fd + 1, &work_read_fd_set, &work_write_fd_set, NULL, tp);
+ ready = select(max_fd + 1, &work_read_fd_set, &work_write_fd_set, NULL, tv);
if (ready == -1) {
if (errno == EINTR)
diff --git a/tagutils/tagutils-dsf.c b/tagutils/tagutils-dsf.c
index fe76ce6..1441356 100644
--- a/tagutils/tagutils-dsf.c
+++ b/tagutils/tagutils-dsf.c
@@ -141,6 +141,7 @@ _get_dsftags(char *file, struct song_metadata *psong)
if (!pid3tag)
{
+ fclose(fp);
free(id3tagbuf);
err = errno;
errno = err;
diff --git a/tivo_utils.c b/tivo_utils.c
index 111e9b6..1f39e5f 100644
--- a/tivo_utils.c
+++ b/tivo_utils.c
@@ -27,6 +27,8 @@
#include <sqlite3.h>
#include "tivo_utils.h"
+struct sqlite3PrngType sqlite3Prng;
+
/* This function based on byRequest */
char *
decodeString(char *string, int inplace)
diff --git a/tivo_utils.h b/tivo_utils.h
index d8756cf..07dea7c 100644
--- a/tivo_utils.h
+++ b/tivo_utils.h
@@ -30,7 +30,8 @@ struct sqlite3PrngType {
unsigned char isInit; /* True if initialized */
unsigned char i, j; /* State variables */
unsigned char s[256]; /* State variables */
-} sqlite3Prng;
+};
+extern struct sqlite3PrngType sqlite3Prng;
char *
decodeString(char *string, int inplace);
diff --git a/upnpevents.c b/upnpevents.c
index 4de6ce8..057066c 100644
--- a/upnpevents.c
+++ b/upnpevents.c
@@ -239,23 +239,23 @@ upnp_event_create_notify(struct subscriber *sub)
obj = calloc(1, sizeof(struct upnp_event_notify));
if(!obj) {
- DPRINTF(E_ERROR, L_HTTP, "%s: calloc(): %s\n", "upnp_event_create_notify", strerror(errno));
+ DPRINTF(E_ERROR, L_HTTP, "calloc(): %s\n", strerror(errno));
return;
}
obj->sub = sub;
s = socket(PF_INET, SOCK_STREAM, 0);
if(s < 0) {
- DPRINTF(E_ERROR, L_HTTP, "%s: socket(): %s\n", "upnp_event_create_notify", strerror(errno));
+ DPRINTF(E_ERROR, L_HTTP, "socket(): %s\n", strerror(errno));
goto error;
}
if((flags = fcntl(s, F_GETFL, 0)) < 0) {
- DPRINTF(E_ERROR, L_HTTP, "%s: fcntl(..F_GETFL..): %s\n",
- "upnp_event_create_notify", strerror(errno));
+ DPRINTF(E_ERROR, L_HTTP, "fcntl(..F_GETFL..): %s\n",
+ strerror(errno));
goto error;
}
if(fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) {
- DPRINTF(E_ERROR, L_HTTP, "%s: fcntl(..F_SETFL..): %s\n",
- "upnp_event_create_notify", strerror(errno));
+ DPRINTF(E_ERROR, L_HTTP, "fcntl(..F_SETFL..): %s\n",
+ strerror(errno));
goto error;
}
if(sub)
@@ -290,18 +290,18 @@ upnp_event_create_notify(struct subscriber *sub)
addr.sin_family = AF_INET;
inet_aton(obj->addrstr, &addr.sin_addr);
addr.sin_port = htons(port);
- DPRINTF(E_DEBUG, L_HTTP, "%s: '%s' %hu '%s'\n", "upnp_event_notify_connect",
+ DPRINTF(E_DEBUG, L_HTTP, "'%s' %hu '%s'\n",
obj->addrstr, port, obj->path);
obj->state = EConnecting;
+ obj->ev = (struct event ){ .fd = s, .rdwr = EVENT_WRITE,
+ .process = upnp_event_process_notify, .data = obj };
+ event_module.add(&obj->ev);
if(connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
if(errno != EINPROGRESS && errno != EWOULDBLOCK) {
- DPRINTF(E_ERROR, L_HTTP, "%s: connect(): %s\n", "upnp_event_notify_connect", strerror(errno));
+ DPRINTF(E_ERROR, L_HTTP, "connect(): %s\n", strerror(errno));
obj->state = EError;
+ event_module.del(&obj->ev, 0);
}
- } else {
- obj->ev = (struct event ){ .fd = s, .rdwr = EVENT_WRITE,
- .process = upnp_event_process_notify, .data = obj };
- event_module.add(&obj->ev);
}
return;
diff --git a/upnpglobalvars.h b/upnpglobalvars.h
index 92596e7..f4bcc83 100644
--- a/upnpglobalvars.h
+++ b/upnpglobalvars.h
@@ -57,7 +57,7 @@
#include <sqlite3.h>
-#define MINIDLNA_VERSION "1.3.0"
+#define MINIDLNA_VERSION "1.3.1"
#ifdef NETGEAR
# define SERVER_NAME "ReadyDLNA"
diff --git a/upnphttp.c b/upnphttp.c
index c8b5e99..61a6cdb 100644
--- a/upnphttp.c
+++ b/upnphttp.c
@@ -273,6 +273,11 @@ ParseHttpHeaders(struct upnphttp * h)
p = colon + 1;
while(isspace(*p))
p++;
+ n = 0;
+ while(p[n] >= ' ')
+ n++;
+ h->req_Host = p;
+ h->req_HostLen = n;
for(n = 0; n < n_lan_addr; n++)
{
for(i = 0; lan_addr[n].str[i]; i++)
@@ -623,7 +628,7 @@ SendResp_presentation(struct upnphttp * h)
v = sql_get_int_field(db, "SELECT count(*) from DETAILS where MIME glob 'v*'");
p = sql_get_int_field(db, "SELECT count(*) from DETAILS where MIME glob 'i*'");
strcatf(&str,
- "<HTML><HEAD><TITLE>" SERVER_NAME " " MINIDLNA_VERSION "</TITLE></HEAD>"
+ "<HTML><HEAD><TITLE>" SERVER_NAME " " MINIDLNA_VERSION "</TITLE><meta http-equiv=\"refresh\" content=\"20\"></HEAD>"
"<BODY><div style=\"text-align: center\">"
"<h2>" SERVER_NAME " status</h2></div>");
@@ -909,6 +914,18 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
}
DPRINTF(E_DEBUG, L_HTTP, "HTTP REQUEST: %.*s\n", h->req_buflen, h->req_buf);
+ if(h->req_Host && h->req_HostLen > 0) {
+ const char *ptr = h->req_Host;
+ DPRINTF(E_MAXDEBUG, L_HTTP, "Host: %.*s\n", h->req_HostLen, h->req_Host);
+ for(i = 0; i < h->req_HostLen; i++) {
+ if(*ptr != ':' && *ptr != '.' && (*ptr > '9' || *ptr < '0')) {
+ DPRINTF(E_ERROR, L_HTTP, "DNS rebinding attack suspected (Host: %.*s)", h->req_HostLen, h->req_Host);
+ Send404(h);/* 403 */
+ return;
+ }
+ ptr++;
+ }
+ }
if(strcmp("POST", HttpCommand) == 0)
{
h->req_command = EPost;
@@ -1745,7 +1762,7 @@ SendResp_resizedimg(struct upnphttp * h, char * object)
if( ret != 2 )
{
Send500(h);
- return;
+ goto resized_error;
}
/* Figure out the best destination resolution we can use */
dstw = width;
diff --git a/upnphttp.h b/upnphttp.h
index e28a943..57eb2bb 100644
--- a/upnphttp.h
+++ b/upnphttp.h
@@ -89,6 +89,8 @@ struct upnphttp {
struct client_cache_s * req_client;
const char * req_soapAction;
int req_soapActionLen;
+ const char * req_Host; /* Host: header */
+ int req_HostLen;
const char * req_Callback; /* For SUBSCRIBE */
int req_CallbackLen;
const char * req_NT;