diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b182169..ce585519 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,6 @@ cmake_minimum_required(VERSION 2.8.12) project(PIHOLE_FTL C) -set(DNSMASQ_VERSION pi-hole-2.87test4-6) +set(DNSMASQ_VERSION pi-hole-2.87test4-18) add_subdirectory(src) diff --git a/src/args.c b/src/args.c index ce85fe4a..64499f5b 100644 --- a/src/args.c +++ b/src/args.c @@ -101,7 +101,26 @@ void parse_args(int argc, char* argv[]) if(strcmp(argv[i], "sql") == 0 || strcmp(argv[i], "sqlite3") == 0 || strcmp(argv[i], "--sqlite3") == 0) + { + // Human-readable table output mode + if(i+1 < argc && strcmp(argv[i+1], "-h") == 0) + { + int argc2 = argc - i + 5 - 2; + char **argv2 = calloc(argc2, sizeof(char*)); + argv2[0] = argv[0]; // Application name + argv2[1] = (char*)"-column"; + argv2[2] = (char*)"-header"; + argv2[3] = (char*)"-nullvalue"; + argv2[4] = (char*)"(null)"; + // i = "sqlite3" + // i+1 = "-h" + for(int j = 0; j < argc - i - 2; j++) + argv2[5 + j] = argv[i + 2 + j]; + exit(sqlite3_shell_main(argc2, argv2)); + } + else exit(sqlite3_shell_main(argc - i, &argv[i])); + } // Implement dnsmasq's test function, no need to prepare the entire FTL // environment (initialize shared memory, lead queries from long-term @@ -322,7 +341,8 @@ void parse_args(int argc, char* argv[]) printf("\t--luac, luac FTL's lua compiler\n"); printf("\tdhcp-discover Discover DHCP servers in the local\n"); printf("\t network\n"); - printf("\tsqlite3 FTL's SQLite3 shell\n"); + printf("\tsql, sqlite3 FTL's SQLite3 shell\n"); + printf("\tsql -h, sqlite3 -h FTL's SQLite3 shell (human-readable mode)\n"); printf("\n\nOnline help: https://github.com/pi-hole/FTL\n"); exit(EXIT_SUCCESS); } diff --git a/src/capabilities.c b/src/capabilities.c index 774e23a4..603cb4f7 100644 --- a/src/capabilities.c +++ b/src/capabilities.c @@ -52,56 +52,52 @@ bool check_capabilities(void) data = calloc(sizeof(*data), capsize); capget(hdr, data); - if(config.debug & DEBUG_CAPS) + logg("***************************************"); + logg("* Linux capability debugging enabled *"); + for(unsigned int i = 0u; i < numCaps; i++) { - logg("***************************************"); - logg("* Linux capability debugging enabled *"); - for(unsigned int i = 0u; i < numCaps; i++) - { - const unsigned int capid = capabilityIDs[i]; - logg("* %-24s (%02u) = %s%s%s *", - capabilityNames[capid], capid, - ((data->permitted & (1 << capid)) ? "P":"-"), - ((data->inheritable & (1 << capid)) ? "I":"-"), - ((data->effective & (1 << capid)) ? "E":"-")); - } - logg("***************************************"); + const unsigned int capid = capabilityIDs[i]; + logg("* %-24s (%02u) = %s%s%s *", + capabilityNames[capid], capid, + ((data->permitted & (1 << capid)) ? "P":"-"), + ((data->inheritable & (1 << capid)) ? "I":"-"), + ((data->effective & (1 << capid)) ? "E":"-")); } + logg("***************************************"); bool capabilities_okay = true; - if (!(data->permitted & (1 << CAP_NET_ADMIN))) + if (!(data->permitted & (1 << CAP_NET_ADMIN)) || + !(data->effective & (1 << CAP_NET_ADMIN))) { // Needed for ARP-injection (used when we're the DHCP server) logg("WARNING: Required Linux capability CAP_NET_ADMIN not available"); capabilities_okay = false; } - if (!(data->permitted & (1 << CAP_NET_RAW))) + if (!(data->permitted & (1 << CAP_NET_RAW)) || + !(data->effective & (1 << CAP_NET_RAW))) { // Needed for raw socket access (necessary for ICMP) logg("WARNING: Required Linux capability CAP_NET_RAW not available"); capabilities_okay = false; } - if (!(data->permitted & (1 << CAP_NET_BIND_SERVICE))) + if (!(data->permitted & (1 << CAP_NET_BIND_SERVICE)) || + !(data->effective & (1 << CAP_NET_BIND_SERVICE))) { // Necessary for dynamic port binding logg("WARNING: Required Linux capability CAP_NET_BIND_SERVICE not available"); capabilities_okay = false; } - if (!(data->permitted & (1 << CAP_SYS_NICE))) + if (!(data->permitted & (1 << CAP_SYS_NICE)) || + !(data->effective & (1 << CAP_SYS_NICE))) { - // Necessary for dynamic port binding + // Necessary for setting higher process priority through nice logg("WARNING: Required Linux capability CAP_SYS_NICE not available"); capabilities_okay = false; } - if (!(data->permitted & (1 << CAP_IPC_LOCK))) + if (!(data->permitted & (1 << CAP_CHOWN)) || + !(data->effective & (1 << CAP_CHOWN))) { - // Necessary for mmap() to work correctly - logg("WARNING: Required Linux capability CAP_IPC_LOCK not available"); - capabilities_okay = false; - } - if (!(data->permitted & (1 << CAP_CHOWN))) - { - // Necessary for chown() to work correctly + // Necessary to chown required files that are owned by another user logg("WARNING: Required Linux capability CAP_CHOWN not available"); capabilities_okay = false; } diff --git a/src/database/shell.c b/src/database/shell.c index cbb3a5ff..d3530909 100644 --- a/src/database/shell.c +++ b/src/database/shell.c @@ -20001,16 +20001,8 @@ static int do_meta_command(char *zLine, ShellState *p){ char *zNewFilename = 0; /* Name of the database file to open */ int iName = 1; /* Index in azArg[] of the filename */ int newFlag = 0; /* True to delete file before opening */ - /* Close the existing database */ - session_close_all(p, -1); - close_db(p->db); - p->db = 0; - p->pAuxDb->zDbFilename = 0; - sqlite3_free(p->pAuxDb->zFreeOnClose); - p->pAuxDb->zFreeOnClose = 0; - p->openMode = SHELL_OPEN_UNSPEC; - p->openFlags = 0; - p->szMax = 0; + int openMode = SHELL_OPEN_UNSPEC; + /* Check for command-line arguments */ for(iName=1; iNameopenMode = SHELL_OPEN_ZIPFILE; + openMode = SHELL_OPEN_ZIPFILE; #endif }else if( optionMatch(z, "append") ){ - p->openMode = SHELL_OPEN_APPENDVFS; + openMode = SHELL_OPEN_APPENDVFS; }else if( optionMatch(z, "readonly") ){ - p->openMode = SHELL_OPEN_READONLY; + openMode = SHELL_OPEN_READONLY; }else if( optionMatch(z, "nofollow") ){ p->openFlags |= SQLITE_OPEN_NOFOLLOW; #ifndef SQLITE_OMIT_DESERIALIZE }else if( optionMatch(z, "deserialize") ){ - p->openMode = SHELL_OPEN_DESERIALIZE; + openMode = SHELL_OPEN_DESERIALIZE; }else if( optionMatch(z, "hexdb") ){ - p->openMode = SHELL_OPEN_HEXDB; + openMode = SHELL_OPEN_HEXDB; }else if( optionMatch(z, "maxsize") && iName+1szMax = integerValue(azArg[++iName]); #endif /* SQLITE_OMIT_DESERIALIZE */ @@ -20046,6 +20038,18 @@ static int do_meta_command(char *zLine, ShellState *p){ zNewFilename = sqlite3_mprintf("%s", z); } } + + /* Close the existing database */ + session_close_all(p, -1); + close_db(p->db); + p->db = 0; + p->pAuxDb->zDbFilename = 0; + sqlite3_free(p->pAuxDb->zFreeOnClose); + p->pAuxDb->zFreeOnClose = 0; + p->openMode = openMode; + p->openFlags = 0; + p->szMax = 0; + /* If a filename is specified, try to open it first */ if( zNewFilename || p->openMode==SHELL_OPEN_HEXDB ){ if( newFlag && !p->bSafeMode ) shellDeleteFile(zNewFilename); @@ -21264,30 +21268,31 @@ static int do_meta_command(char *zLine, ShellState *p){ static const struct { const char *zCtrlName; /* Name of a test-control option */ int ctrlCode; /* Integer code for that option */ + int unSafe; /* Not valid for --safe mode */ const char *zUsage; /* Usage notes */ } aCtrl[] = { - { "always", SQLITE_TESTCTRL_ALWAYS, "BOOLEAN" }, - { "assert", SQLITE_TESTCTRL_ASSERT, "BOOLEAN" }, - /*{ "benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, "" },*/ - /*{ "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, "" },*/ - { "byteorder", SQLITE_TESTCTRL_BYTEORDER, "" }, - { "extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,"BOOLEAN" }, - /*{ "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, "" },*/ - { "imposter", SQLITE_TESTCTRL_IMPOSTER, "SCHEMA ON/OFF ROOTPAGE"}, - { "internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS, "" }, - { "localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,"BOOLEAN" }, - { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT, "BOOLEAN" }, - { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS, "DISABLE-MASK" }, + { "always", SQLITE_TESTCTRL_ALWAYS, 1, "BOOLEAN" }, + { "assert", SQLITE_TESTCTRL_ASSERT, 1, "BOOLEAN" }, + /*{ "benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS,1, "" },*/ + /*{ "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "" },*/ + { "byteorder", SQLITE_TESTCTRL_BYTEORDER, 0, "" }, + { "extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,0,"BOOLEAN" }, + /*{ "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, 1,"" },*/ + { "imposter", SQLITE_TESTCTRL_IMPOSTER,1,"SCHEMA ON/OFF ROOTPAGE"}, + { "internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS,0,"" }, + { "localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,0,"BOOLEAN" }, + { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT,1, "BOOLEAN" }, + { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS,0,"DISABLE-MASK" }, #ifdef YYCOVERAGE - { "parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE, "" }, + { "parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE,0,"" }, #endif - { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE, "OFFSET " }, - { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE, "" }, - { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE, "" }, - { "prng_seed", SQLITE_TESTCTRL_PRNG_SEED, "SEED ?db?" }, - { "seek_count", SQLITE_TESTCTRL_SEEK_COUNT, "" }, - { "sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, "NMAX" }, - { "tune", SQLITE_TESTCTRL_TUNE, "ID VALUE" }, + { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE,0, "OFFSET " }, + { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE,0, "" }, + { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE, 0, "" }, + { "prng_seed", SQLITE_TESTCTRL_PRNG_SEED, 0, "SEED ?db?" }, + { "seek_count", SQLITE_TESTCTRL_SEEK_COUNT, 0, "" }, + { "sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, 0, "NMAX" }, + { "tune", SQLITE_TESTCTRL_TUNE, 1, "ID VALUE" }, }; int testctrl = -1; int iCtrl = -1; @@ -21335,6 +21340,11 @@ static int do_meta_command(char *zLine, ShellState *p){ if( testctrl<0 ){ utf8_printf(stderr,"Error: unknown test-control: %s\n" "Use \".testctrl --help\" for help\n", zCmd); + }else if( aCtrl[iCtrl].unSafe && p->bSafeMode ){ + utf8_printf(stderr, + "line %d: \".testctrl %s\" may not be used in safe mode\n", + p->lineno, aCtrl[iCtrl].zCtrlName); + exit(1); }else{ switch(testctrl){ diff --git a/src/database/sqlite3.c b/src/database/sqlite3.c index 4f3deb17..fba450a7 100644 --- a/src/database/sqlite3.c +++ b/src/database/sqlite3.c @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.37.0. By combining all the individual C code files into this +** version 3.37.1. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -452,9 +452,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.37.0" -#define SQLITE_VERSION_NUMBER 3037000 -#define SQLITE_SOURCE_ID "2021-11-27 14:13:22 bd41822c7424d393a30e92ff6cb254d25c26769889c1499a18a0b9339f5d6c8a" +#define SQLITE_VERSION "3.37.1" +#define SQLITE_VERSION_NUMBER 3037001 +#define SQLITE_SOURCE_ID "2021-12-30 15:30:28 378629bf2ea546f73eee84063c5358439a12f7300e433f18c9e1bddd948dea62" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -104085,7 +104085,7 @@ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){ return ExprHasProperty(p, EP_CanBeNull) || p->y.pTab==0 || /* Reference to column of index on expression */ (p->iColumn>=0 - && ALWAYS(p->y.pTab->aCol!=0) /* Defense against OOM problems */ + && p->y.pTab->aCol!=0 /* Possible due to prior error */ && p->y.pTab->aCol[p->iColumn].notNull==0); default: return 1; @@ -126000,6 +126000,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( if( onError==OE_Replace /* IPK rule is REPLACE */ && onError!=overrideError /* Rules for other constraints are different */ && pTab->pIndex /* There exist other constraints */ + && !upsertIpkDelay /* IPK check already deferred by UPSERT */ ){ ipkTop = sqlite3VdbeAddOp0(v, OP_Goto)+1; VdbeComment((v, "defer IPK REPLACE until last")); @@ -126408,6 +126409,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( if( ipkTop ){ sqlite3VdbeGoto(v, ipkTop); VdbeComment((v, "Do IPK REPLACE")); + assert( ipkBottom>0 ); sqlite3VdbeJumpHere(v, ipkBottom); } @@ -133005,6 +133007,7 @@ static int sqlite3LockAndPrepare( ** reset is considered a permanent error. */ rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail); assert( rc==SQLITE_OK || *ppStmt==0 ); + if( rc==SQLITE_OK || db->mallocFailed ) break; }while( rc==SQLITE_ERROR_RETRY || (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt++)==0) ); sqlite3BtreeLeaveAll(db); @@ -169411,6 +169414,8 @@ SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ if( newLimit>=0 ){ /* IMP: R-52476-28732 */ if( newLimit>aHardLimit[limitId] ){ newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */ + }else if( newLimit<1 && limitId==SQLITE_LIMIT_LENGTH ){ + newLimit = 1; } db->aLimit[limitId] = newLimit; } @@ -170814,12 +170819,16 @@ SQLITE_API int sqlite3_test_control(int op, ...){ */ case SQLITE_TESTCTRL_IMPOSTER: { sqlite3 *db = va_arg(ap, sqlite3*); + int iDb; sqlite3_mutex_enter(db->mutex); - db->init.iDb = sqlite3FindDbName(db, va_arg(ap,const char*)); - db->init.busy = db->init.imposterTable = va_arg(ap,int); - db->init.newTnum = va_arg(ap,int); - if( db->init.busy==0 && db->init.newTnum>0 ){ - sqlite3ResetAllSchemasOfConnection(db); + iDb = sqlite3FindDbName(db, va_arg(ap,const char*)); + if( iDb>=0 ){ + db->init.iDb = iDb; + db->init.busy = db->init.imposterTable = va_arg(ap,int); + db->init.newTnum = va_arg(ap,int); + if( db->init.busy==0 && db->init.newTnum>0 ){ + sqlite3ResetAllSchemasOfConnection(db); + } } sqlite3_mutex_leave(db->mutex); break; @@ -177073,7 +177082,7 @@ SQLITE_PRIVATE void sqlite3Fts3DoclistPrev( assert( nDoclist>0 ); assert( *pbEof==0 ); - assert( p || *piDocid==0 ); + assert_fts3_nc( p || *piDocid==0 ); assert( !p || (p>aDoclist && p<&aDoclist[nDoclist]) ); if( p==0 ){ @@ -224759,8 +224768,12 @@ static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){ int iRowidOff; iRowidOff = fts5LeafFirstRowidOff(pNew); if( iRowidOff ){ - pIter->pLeaf = pNew; - pIter->iLeafOffset = iRowidOff; + if( iRowidOff>=pNew->szLeaf ){ + p->rc = FTS5_CORRUPT; + }else{ + pIter->pLeaf = pNew; + pIter->iLeafOffset = iRowidOff; + } } } @@ -232489,7 +232502,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2021-11-27 14:13:22 bd41822c7424d393a30e92ff6cb254d25c26769889c1499a18a0b9339f5d6c8a", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2021-12-30 15:30:28 378629bf2ea546f73eee84063c5358439a12f7300e433f18c9e1bddd948dea62", -1, SQLITE_TRANSIENT); } /* diff --git a/src/database/sqlite3.h b/src/database/sqlite3.h index 1247048a..393e9d20 100644 --- a/src/database/sqlite3.h +++ b/src/database/sqlite3.h @@ -146,9 +146,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.37.0" -#define SQLITE_VERSION_NUMBER 3037000 -#define SQLITE_SOURCE_ID "2021-11-27 14:13:22 bd41822c7424d393a30e92ff6cb254d25c26769889c1499a18a0b9339f5d6c8a" +#define SQLITE_VERSION "3.37.1" +#define SQLITE_VERSION_NUMBER 3037001 +#define SQLITE_SOURCE_ID "2021-12-30 15:30:28 378629bf2ea546f73eee84063c5358439a12f7300e433f18c9e1bddd948dea62" /* ** CAPI3REF: Run-Time Library Version Numbers diff --git a/src/dnsmasq/cache.c b/src/dnsmasq/cache.c index 90eb139b..449c884f 100644 --- a/src/dnsmasq/cache.c +++ b/src/dnsmasq/cache.c @@ -414,7 +414,7 @@ static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned s if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name)) { /* Don't delete DNSSEC in favour of a CNAME, they can co-exist */ - if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_SRV)) || + if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_SRV | F_NXDOMAIN)) || (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS)))) { if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) @@ -1731,9 +1731,7 @@ void get_dnsmasq_cache_info(struct cache_info *ci) const time_t now = time(NULL); for (int i=0; i < hash_size; i++) for (struct crec *cache = hash_table[i]; cache; cache = cache->hash_next) - if(cache->flags & F_IMMORTAL) - ci->immortal++; - else if(cache->ttd >= now) + if(cache->ttd >= now || cache->flags & F_IMMORTAL) { if (cache->flags & F_IPV4) ci->valid.ipv4++; @@ -1751,6 +1749,9 @@ void get_dnsmasq_cache_info(struct cache_info *ci) #endif else ci->valid.other++; + + if(cache->flags & F_IMMORTAL) + ci->immortal++; } else ci->expired++; diff --git a/src/dnsmasq/dhcp-common.c b/src/dnsmasq/dhcp-common.c index 37b2f38d..80c1538e 100644 --- a/src/dnsmasq/dhcp-common.c +++ b/src/dnsmasq/dhcp-common.c @@ -985,11 +985,27 @@ void log_context(int family, struct dhcp_context *context) void log_relay(int family, struct dhcp_relay *relay) { + int broadcast = relay->server.addr4.s_addr == 0; inet_ntop(family, &relay->local, daemon->addrbuff, ADDRSTRLEN); inet_ntop(family, &relay->server, daemon->namebuff, ADDRSTRLEN); +#ifdef HAVE_DHCP6 + struct in6_addr multicast; + + inet_pton(AF_INET6, ALL_SERVERS, &multicast); + + if (family == AF_INET6) + broadcast = IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast); +#endif + + if (relay->interface) - my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface); + { + if (broadcast) + my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s via %s"), daemon->addrbuff, relay->interface); + else + my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface); + } else my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s"), daemon->addrbuff, daemon->namebuff); } diff --git a/src/dnsmasq/dhcp.c b/src/dnsmasq/dhcp.c index e500bc2f..2c1272f5 100644 --- a/src/dnsmasq/dhcp.c +++ b/src/dnsmasq/dhcp.c @@ -1095,14 +1095,35 @@ static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, to.sa.sa_family = AF_INET; to.in.sin_addr = relay->server.addr4; to.in.sin_port = htons(daemon->dhcp_server_port); - + + /* Broadcasting to server. */ + if (relay->server.addr4.s_addr == 0) + { + struct ifreq ifr; + + if (relay->interface) + safe_strncpy(ifr.ifr_name, relay->interface, IF_NAMESIZE); + + if (!relay->interface || strchr(relay->interface, '*') || + ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) == -1) + { + my_syslog(MS_DHCP | LOG_ERR, _("Cannot broadcast DHCP relay via interface %s"), relay->interface); + return 1; + } + + to.in.sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; + } + send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0); if (option_bool(OPT_LOG_OPTS)) { inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN); - inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ); - my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2); + if (relay->server.addr4.s_addr == 0) + snprintf(daemon->dhcp_buff2, DHCP_BUFF_SZ, _("broadcast via %s"), relay->interface); + else + inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ); + my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2); } /* Save this for replies */ diff --git a/src/dnsmasq/dhcp6-protocol.h b/src/dnsmasq/dhcp6-protocol.h index ebe8dfd4..f1d09914 100644 --- a/src/dnsmasq/dhcp6-protocol.h +++ b/src/dnsmasq/dhcp6-protocol.h @@ -55,6 +55,8 @@ #define OPTION6_RECONF_ACCEPT 20 #define OPTION6_DNS_SERVER 23 #define OPTION6_DOMAIN_SEARCH 24 +#define OPTION6_IA_PD 25 +#define OPTION6_IAPREFIX 26 #define OPTION6_REFRESH_TIME 32 #define OPTION6_REMOTE_ID 37 #define OPTION6_SUBSCRIBER_ID 38 diff --git a/src/dnsmasq/dhcp6.c b/src/dnsmasq/dhcp6.c index ae1f5c16..c061879c 100644 --- a/src/dnsmasq/dhcp6.c +++ b/src/dnsmasq/dhcp6.c @@ -135,9 +135,8 @@ void dhcp6_packet(time_t now) if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name)) return; - if ((port = relay_reply6(&from, sz, ifr.ifr_name)) != 0) + if (relay_reply6(&from, sz, ifr.ifr_name)) { - from.sin6_port = htons(port); while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, save_counter(-1), 0, (struct sockaddr *)&from, sizeof(from)))); diff --git a/src/dnsmasq/dnsmasq.c b/src/dnsmasq/dnsmasq.c index e9c4d942..f9204930 100644 --- a/src/dnsmasq/dnsmasq.c +++ b/src/dnsmasq/dnsmasq.c @@ -743,7 +743,11 @@ int main_dnsmasq (int argc, char **argv) /* if we are to run scripts, we need to fork a helper before dropping root. */ daemon->helperfd = -1; #ifdef HAVE_SCRIPT - if ((daemon->dhcp || daemon->dhcp6 || option_bool(OPT_TFTP) || option_bool(OPT_SCRIPT_ARP)) && + if ((daemon->dhcp || + daemon->dhcp6 || + daemon->relay6 || + option_bool(OPT_TFTP) || + option_bool(OPT_SCRIPT_ARP)) && (daemon->lease_change_command || daemon->luascript)) daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd); #endif @@ -1152,6 +1156,10 @@ int main_dnsmasq (int argc, char **argv) while (helper_buf_empty() && do_tftp_script_run()); # endif +# ifdef HAVE_DHCP6 + while (helper_buf_empty() && do_snoop_script_run()); +# endif + if (!helper_buf_empty()) poll_listen(daemon->helperfd, POLLOUT); #else @@ -1713,6 +1721,11 @@ static void poll_resolv(int force, int do_reload, time_t now) } else { + /* If we're delaying things, we don't call check_servers(), but + reload_servers() may have deleted some servers, rendering the server_array + invalid, so just rebuild that here. Once reload_servers() succeeds, + we call check_servers() above, which calls build_server_array itself. */ + build_server_array(); latest->mtime = 0; if (!warned) { diff --git a/src/dnsmasq/dnsmasq.h b/src/dnsmasq/dnsmasq.h index 504e9444..a281c990 100644 --- a/src/dnsmasq/dnsmasq.h +++ b/src/dnsmasq/dnsmasq.h @@ -790,6 +790,7 @@ struct frec { #define ACTION_TFTP 5 #define ACTION_ARP 6 #define ACTION_ARP_DEL 7 +#define ACTION_RELAY_SNOOP 8 #define LEASE_NEW 1 /* newly created */ #define LEASE_CHANGED 2 /* modified */ @@ -1088,6 +1089,13 @@ struct dhcp_relay { union all_addr local, server; char *interface; /* Allowable interface for replies from server, and dest for IPv6 multicast */ int iface_index; /* working - interface in which requests arrived, for return */ +#ifdef HAVE_SCRIPT + struct snoop_record { + struct in6_addr client, prefix; + int prefix_len; + struct snoop_record *next; + } *snoop_records; +#endif struct dhcp_relay *current, *next; }; @@ -1239,13 +1247,18 @@ extern struct daemon { unsigned char *duid; struct iovec outpacket; int dhcp6fd, icmp6fd; +# ifdef HAVE_SCRIPT + struct snoop_record *free_snoops; +# endif #endif + /* DBus stuff */ /* void * here to avoid depending on dbus headers outside dbus.c */ void *dbus; #ifdef HAVE_DBUS struct watch *watches; #endif + /* UBus stuff */ #ifdef HAVE_UBUS /* void * here to avoid depending on ubus headers outside ubus.c */ @@ -1659,6 +1672,9 @@ void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer); void queue_arp(int action, unsigned char *mac, int maclen, int family, union all_addr *addr); int helper_buf_empty(void); +#ifdef HAVE_DHCP6 +void queue_relay_snoop(struct in6_addr *client, int if_index, struct in6_addr *prefix, int prefix_len); +#endif #endif /* tftp.c */ @@ -1704,7 +1720,10 @@ unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *if void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id, time_t now); -unsigned short relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface); +int relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface); +# ifdef HAVE_SCRIPT +int do_snoop_script_run(void); +# endif #endif /* dhcp-common.c */ diff --git a/src/dnsmasq/domain-match.c b/src/dnsmasq/domain-match.c index ba58610f..4e010920 100644 --- a/src/dnsmasq/domain-match.c +++ b/src/dnsmasq/domain-match.c @@ -395,7 +395,7 @@ int is_local_answer(time_t now, int first, char *name) size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, char *limit, int first, int last, int ede) { - int trunc = 0; + int trunc = 0, anscount = 0; unsigned char *p; int start; union all_addr addr; @@ -419,7 +419,7 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header addr.addr4 = srv->addr; if (add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr)) - header->ancount = htons(ntohs(header->ancount) + 1); + anscount++; log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, name, (union all_addr *)&addr, NULL, 0); } @@ -434,13 +434,14 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header addr.addr6 = srv->addr; if (add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr)) - header->ancount = htons(ntohs(header->ancount) + 1); + anscount++; log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, name, (union all_addr *)&addr, NULL, 0); } if (trunc) header->hb3 |= HB3_TC; - + header->ancount = htons(anscount); + return p - (unsigned char *)header; } @@ -541,22 +542,39 @@ static int order_qsort(const void *a, const void *b) return rc; } +/* Must be called before add_update_server() to set daemon->servers_tail */ void mark_servers(int flag) { - struct server *serv; + struct server *serv, **up; + daemon->servers_tail = NULL; + /* mark everything with argument flag */ for (serv = daemon->servers; serv; serv = serv->next) - if (serv->flags & flag) - serv->flags |= SERV_MARK; - else - serv->flags &= ~SERV_MARK; + { + if (serv->flags & flag) + serv->flags |= SERV_MARK; + else + serv->flags &= ~SERV_MARK; - for (serv = daemon->local_domains; serv; serv = serv->next) - if (serv->flags & flag) - serv->flags |= SERV_MARK; - else - serv->flags &= ~SERV_MARK; + daemon->servers_tail = serv; + } + + /* --address etc is different: since they are expected to be + 1) numerous and 2) not reloaded often. We just delete + and recreate. */ + if (flag) + for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = serv->next) + { + if (serv->flags & flag) + { + *up = serv->next; + free(serv->domain); + free(serv); + } + else + up = &serv->next; + } } void cleanup_servers(void) @@ -564,7 +582,7 @@ void cleanup_servers(void) struct server *serv, *tmp, **up; /* unlink and free anything still marked. */ - for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp) + for (serv = daemon->servers, up = &daemon->servers, daemon->servers_tail = NULL; serv; serv = tmp) { tmp = serv->next; if (serv->flags & SERV_MARK) @@ -580,19 +598,6 @@ void cleanup_servers(void) daemon->servers_tail = serv; } } - - for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = tmp) - { - tmp = serv->next; - if (serv->flags & SERV_MARK) - { - *up = serv->next; - free(serv->domain); - free(serv); - } - else - up = &serv->next; - } } int add_update_server(int flags, @@ -625,36 +630,17 @@ int add_update_server(int flags, if (!alloc_domain) return 0; - - /* See if there is a suitable candidate, and unmark - only do this for forwarding servers, not - address or local, to avoid delays on large numbers. */ + if (flags & SERV_IS_LOCAL) - for (serv = daemon->servers; serv; serv = serv->next) - if ((serv->flags & SERV_MARK) && - hostname_isequal(alloc_domain, serv->domain)) - break; - - if (serv) - { - free(alloc_domain); - alloc_domain = serv->domain; - } - else { size_t size; - - if (flags & SERV_IS_LOCAL) - { - if (flags & SERV_6ADDR) - size = sizeof(struct serv_addr6); - else if (flags & SERV_4ADDR) - size = sizeof(struct serv_addr4); - else - size = sizeof(struct serv_local); - } + + if (flags & SERV_6ADDR) + size = sizeof(struct serv_addr6); + else if (flags & SERV_4ADDR) + size = sizeof(struct serv_addr4); else - size = sizeof(struct server); + size = sizeof(struct serv_local); if (!(serv = whine_malloc(size))) { @@ -662,19 +648,53 @@ int add_update_server(int flags, return 0; } - if (flags & SERV_IS_LOCAL) + serv->next = daemon->local_domains; + daemon->local_domains = serv; + + if (flags & SERV_4ADDR) + ((struct serv_addr4*)serv)->addr = local_addr->addr4; + + if (flags & SERV_6ADDR) + ((struct serv_addr6*)serv)->addr = local_addr->addr6; + } + else + { + /* Upstream servers. See if there is a suitable candidate, if so unmark + and move to the end of the list, for order. The entry found may already + be at the end. */ + struct server **up, *tmp; + + for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp) { - serv->next = daemon->local_domains; - daemon->local_domains = serv; + tmp = serv->next; + if ((serv->flags & SERV_MARK) && + hostname_isequal(alloc_domain, serv->domain)) + { + /* Need to move down? */ + if (serv->next) + { + *up = serv->next; + daemon->servers_tail->next = serv; + daemon->servers_tail = serv; + serv->next = NULL; + } + break; + } + } - if (flags & SERV_4ADDR) - ((struct serv_addr4*)serv)->addr = local_addr->addr4; - - if (flags & SERV_6ADDR) - ((struct serv_addr6*)serv)->addr = local_addr->addr6; + if (serv) + { + free(alloc_domain); + alloc_domain = serv->domain; } else { + if (!(serv = whine_malloc(sizeof(struct server)))) + { + free(alloc_domain); + return 0; + } + memset(serv, 0, sizeof(struct server)); /* Add to the end of the chain, for order */ @@ -683,20 +703,20 @@ int add_update_server(int flags, else daemon->servers = serv; daemon->servers_tail = serv; - + } + #ifdef HAVE_LOOP - serv->uid = rand32(); + serv->uid = rand32(); #endif - if (interface) - safe_strncpy(serv->interface, interface, sizeof(serv->interface)); - if (addr) - serv->addr = *addr; - if (source_addr) - serv->source_addr = *source_addr; - } + if (interface) + safe_strncpy(serv->interface, interface, sizeof(serv->interface)); + if (addr) + serv->addr = *addr; + if (source_addr) + serv->source_addr = *source_addr; } - + serv->flags = flags; serv->domain = alloc_domain; serv->domain_len = strlen(alloc_domain); diff --git a/src/dnsmasq/edns0.c b/src/dnsmasq/edns0.c index 9cf1d114..5de6cb22 100644 --- a/src/dnsmasq/edns0.c +++ b/src/dnsmasq/edns0.c @@ -460,31 +460,33 @@ static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned struct umbrella_opt opt = {{"ODNS"}, UMBRELLA_VERSION, 0, {}}; u8 *u = &opt.fields[0]; - - if (daemon->umbrella_org) { - PUTSHORT(UMBRELLA_ORG, u); - PUTLONG(daemon->umbrella_org, u); - } - int family = source->sa.sa_family; - PUTSHORT(family == AF_INET ? UMBRELLA_IPV4 : UMBRELLA_IPV6, u); int size = family == AF_INET ? INADDRSZ : IN6ADDRSZ; + + if (daemon->umbrella_org) + { + PUTSHORT(UMBRELLA_ORG, u); + PUTLONG(daemon->umbrella_org, u); + } + + PUTSHORT(family == AF_INET ? UMBRELLA_IPV4 : UMBRELLA_IPV6, u); memcpy(u, get_addrp(source, family), size); u += size; + + if (option_bool(OPT_UMBRELLA_DEVID)) + { + PUTSHORT(UMBRELLA_DEVICE, u); + memcpy(u, (char *)&daemon->umbrella_device, UMBRELLA_DEVICESZ); + u += UMBRELLA_DEVICESZ; + } - if (option_bool(OPT_UMBRELLA_DEVID)) { - PUTSHORT(UMBRELLA_DEVICE, u); - memcpy(u, (char *)&daemon->umbrella_device, UMBRELLA_DEVICESZ); - u += UMBRELLA_DEVICESZ; - } - - if (daemon->umbrella_asset) { - PUTSHORT(UMBRELLA_ASSET, u); - PUTLONG(daemon->umbrella_asset, u); - } - - int len = u - &opt.magic[0]; - return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, len, 0, 1); + if (daemon->umbrella_asset) + { + PUTSHORT(UMBRELLA_ASSET, u); + PUTLONG(daemon->umbrella_asset, u); + } + + return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, u - (u8 *)&opt, 0, 1); } /* Set *check_subnet if we add a client subnet option, which needs to checked diff --git a/src/dnsmasq/forward.c b/src/dnsmasq/forward.c index 22d11fbb..e1061ecd 100644 --- a/src/dnsmasq/forward.c +++ b/src/dnsmasq/forward.c @@ -1666,6 +1666,9 @@ void receive_query(struct listener *listen, time_t now) if (m >= 1) { +#ifdef HAVE_DUMPFILE + dump_packet(DUMP_REPLY, daemon->packet, m, NULL, &source_addr); +#endif send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, m, &source_addr, &dst_addr, if_index); daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++; @@ -1679,6 +1682,9 @@ void receive_query(struct listener *listen, time_t now) local_auth, do_bit, have_pseudoheader); if (m >= 1) { +#ifdef HAVE_DUMPFILE + dump_packet(DUMP_REPLY, daemon->packet, m, NULL, &source_addr); +#endif #if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS) if (local_auth) if (option_bool(OPT_CMARK_ALST_EN) && have_mark && ((u32)mark & daemon->allowlist_mask)) @@ -1978,7 +1984,7 @@ unsigned char *tcp_request(int confd, time_t now, bool piholeblocked = false; /**********************************************/ - if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1) + if (!packet || getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1) return packet; #ifdef HAVE_CONNTRACK diff --git a/src/dnsmasq/helper.c b/src/dnsmasq/helper.c index 8821b975..aba66389 100644 --- a/src/dnsmasq/helper.c +++ b/src/dnsmasq/helper.c @@ -238,8 +238,13 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) is6 = (data.flags != AF_INET); data.action = ACTION_ARP; } - else - continue; + else if (data.action == ACTION_RELAY_SNOOP) + { + is6 = 1; + action_str = "relay-snoop"; + } + else + continue; /************************** Pi-hole modification **************************/ FTL_log_helper(1, action_str); @@ -295,7 +300,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) char *dot; hostname = (char *)buf; hostname[data.hostname_len - 1] = 0; - if (data.action != ACTION_TFTP) + if (data.action != ACTION_TFTP && data.action != ACTION_RELAY_SNOOP) { if (!legal_hostname(hostname)) hostname = NULL; @@ -341,6 +346,24 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) lua_call(lua, 2, 0); /* pass 2 values, expect 0 */ } } + else if (data.action == ACTION_RELAY_SNOOP) + { + lua_getglobal(lua, "snoop"); + if (lua_type(lua, -1) != LUA_TFUNCTION) + lua_pop(lua, 1); /* tftp function optional */ + else + { + lua_pushstring(lua, action_str); /* arg1 - action */ + lua_newtable(lua); /* arg2 - data table */ + lua_pushstring(lua, daemon->addrbuff); + lua_setfield(lua, -2, "client_address"); + lua_pushstring(lua, hostname); + lua_setfield(lua, -2, "prefix"); + lua_pushstring(lua, data.interface); + lua_setfield(lua, -2, "client_interface"); + lua_call(lua, 2, 0); /* pass 2 values, expect 0 */ + } + } else if (data.action == ACTION_ARP) { lua_getglobal(lua, "arp"); @@ -441,8 +464,8 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) buf = grab_extradata_lua(buf, end, "relay_address"); else if (data.giaddr.s_addr != 0) { - inet_ntop(AF_INET, &data.giaddr, daemon->addrbuff, ADDRSTRLEN); - lua_pushstring(lua, daemon->addrbuff); + inet_ntop(AF_INET, &data.giaddr, daemon->dhcp_buff2, ADDRSTRLEN); + lua_pushstring(lua, daemon->dhcp_buff2); lua_setfield(lua, -2, "relay_address"); } @@ -562,7 +585,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) close(pipeout[1]); } - if (data.action != ACTION_TFTP && data.action != ACTION_ARP) + if (data.action != ACTION_TFTP && data.action != ACTION_ARP && data.action != ACTION_RELAY_SNOOP) { #ifdef HAVE_DHCP6 my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 : NULL, &err); @@ -624,7 +647,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) { const char *giaddr = NULL; if (data.giaddr.s_addr != 0) - giaddr = inet_ntop(AF_INET, &data.giaddr, daemon->addrbuff, ADDRSTRLEN); + giaddr = inet_ntop(AF_INET, &data.giaddr, daemon->dhcp_buff2, ADDRSTRLEN); my_setenv("DNSMASQ_RELAY_ADDRESS", giaddr, &err); } @@ -649,6 +672,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) fcntl(event_fd, F_SETFD, i | FD_CLOEXEC); close(pipefd[0]); + if (data.action == ACTION_RELAY_SNOOP) + strcpy(daemon->packet, data.interface); + /**************************** Pi-hole modification ****************************/ FTL_log_helper(5, daemon->lease_change_command, action_str, (is6 && data.action != ACTION_ARP) ? daemon->packet : daemon->dhcp_buff, @@ -828,6 +854,29 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n bytes_in_buf = p - (unsigned char *)buf; } +#ifdef HAVE_DHCP6 +void queue_relay_snoop(struct in6_addr *client, int if_index, struct in6_addr *prefix, int prefix_len) +{ + /* no script */ + if (daemon->helperfd == -1) + return; + + inet_ntop(AF_INET6, prefix, daemon->addrbuff, ADDRSTRLEN); + + /* 5 for /nnn and zero on the end of the prefix. */ + buff_alloc(sizeof(struct script_data) + ADDRSTRLEN + 5); + memset(buf, 0, sizeof(struct script_data)); + + buf->action = ACTION_RELAY_SNOOP; + buf->addr6 = *client; + buf->hostname_len = sprintf((char *)(buf+1), "%s/%u", daemon->addrbuff, prefix_len) + 1; + + indextoname(daemon->dhcp6fd, if_index, buf->interface); + + bytes_in_buf = sizeof(struct script_data) + buf->hostname_len; +} +#endif + #ifdef HAVE_TFTP /* This nastily re-uses DHCP-fields for TFTP stuff */ void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer) diff --git a/src/dnsmasq/network.c b/src/dnsmasq/network.c index 1572ae99..5e5c3b91 100644 --- a/src/dnsmasq/network.c +++ b/src/dnsmasq/network.c @@ -492,11 +492,11 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label, } if (addr->sa.sa_family == AF_INET && - !iface_check(AF_INET, (union all_addr *)&addr->in.sin_addr, ifr.ifr_name, &auth_dns)) + !iface_check(AF_INET, (union all_addr *)&addr->in.sin_addr, label, &auth_dns)) return 1; if (addr->sa.sa_family == AF_INET6 && - !iface_check(AF_INET6, (union all_addr *)&addr->in6.sin6_addr, ifr.ifr_name, &auth_dns)) + !iface_check(AF_INET6, (union all_addr *)&addr->in6.sin6_addr, label, &auth_dns)) return 1; #ifdef HAVE_DHCP diff --git a/src/dnsmasq/option.c b/src/dnsmasq/option.c index d164b996..b0611a52 100644 --- a/src/dnsmasq/option.c +++ b/src/dnsmasq/option.c @@ -361,7 +361,7 @@ static const struct myoption opts[] = { "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID }, { "dynamic-host", 1, 0, LOPT_DYNHOST }, { "log-debug", 0, 0, LOPT_LOG_DEBUG }, - { "umbrella", 2, 0, LOPT_UMBRELLA }, + { "umbrella", 2, 0, LOPT_UMBRELLA }, { "quiet-tftp", 0, 0, LOPT_QUIET_TFTP }, { NULL, 0, 0, 0 } }; @@ -2523,39 +2523,48 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma case LOPT_UMBRELLA: /* --umbrella */ set_option_bool(OPT_UMBRELLA); - while (arg) { - comma = split(arg); - if (strstr(arg, "deviceid:")) { - arg += 9; - if (strlen(arg) != 16) - ret_err(gen_err); - for (char *p = arg; *p; p++) { - if (!isxdigit((int)*p)) - ret_err(gen_err); - } - set_option_bool(OPT_UMBRELLA_DEVID); - - u8 *u = daemon->umbrella_device; - char word[3]; - for (u8 i = 0; i < sizeof(daemon->umbrella_device); i++, arg+=2) { - memcpy(word, &(arg[0]), 2); - *u++ = strtoul(word, NULL, 16); - } - } - else if (strstr(arg, "orgid:")) { - if (!strtoul_check(arg+6, &daemon->umbrella_org)) { - ret_err(gen_err); - } - } - else if (strstr(arg, "assetid:")) { - if (!strtoul_check(arg+8, &daemon->umbrella_asset)) { - ret_err(gen_err); - } - } - arg = comma; - } + while (arg) + { + comma = split(arg); + if (strstr(arg, "deviceid:")) + { + char *p; + u8 *u = daemon->umbrella_device; + char word[3]; + + arg += 9; + if (strlen(arg) != 16) + ret_err(gen_err); + + for (p = arg; *p; p++) + if (!isxdigit((int)*p)) + ret_err(gen_err); + + set_option_bool(OPT_UMBRELLA_DEVID); + + for (i = 0; i < (int)sizeof(daemon->umbrella_device); i++, arg+=2) + { + memcpy(word, &(arg[0]), 2); + *u++ = strtoul(word, NULL, 16); + } + } + else if (strstr(arg, "orgid:")) + { + if (!strtoul_check(arg+6, &daemon->umbrella_org)) + ret_err(gen_err); + } + else if (strstr(arg, "assetid:")) + { + if (!strtoul_check(arg+8, &daemon->umbrella_asset)) + ret_err(gen_err); + } + else + ret_err(gen_err); + + arg = comma; + } break; - + case LOPT_ADD_MAC: /* --add-mac */ if (!arg) set_option_bool(OPT_ADD_MAC); @@ -4132,7 +4141,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma case LOPT_SUBSCR: /* --dhcp-subscrid */ { unsigned char *p; - int dig = 0; + int dig, colon; struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor)); if (!(comma = split(arg))) @@ -4156,13 +4165,16 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma else comma = arg; - for (p = (unsigned char *)comma; *p; p++) + for (dig = 0, colon = 0, p = (unsigned char *)comma; *p; p++) if (isxdigit(*p)) dig = 1; - else if (*p != ':') + else if (*p == ':') + colon = 1; + else break; + unhide_metas(comma); - if (option == 'U' || option == 'j' || *p || !dig) + if (option == 'U' || option == 'j' || *p || !dig || !colon) { new->len = strlen(comma); new->data = opt_malloc(new->len); @@ -4285,26 +4297,56 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } } break; - + case LOPT_RELAY: /* --dhcp-relay */ { struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay)); - comma = split(arg); - new->interface = opt_string_alloc(split(comma)); + char *two = split(arg); + char *three = split(two); + new->iface_index = 0; - if (comma && inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server)) + + if (two) { - new->next = daemon->relay4; - daemon->relay4 = new; - } + if (inet_pton(AF_INET, arg, &new->local)) + { + if (!inet_pton(AF_INET, two, &new->server)) + { + new->server.addr4.s_addr = 0; + + /* Fail for three arg version where there are not two addresses. + Also fail when broadcasting to wildcard address. */ + if (three || strchr(two, '*')) + two = NULL; + else + three = two; + } + + new->next = daemon->relay4; + daemon->relay4 = new; + } #ifdef HAVE_DHCP6 - else if (comma && inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server)) - { - new->next = daemon->relay6; - daemon->relay6 = new; - } + else if (inet_pton(AF_INET6, arg, &new->local)) + { + if (!inet_pton(AF_INET6, two, &new->server)) + { + inet_pton(AF_INET6, ALL_SERVERS, &new->server.addr6); + /* Fail for three arg version where there are not two addresses. + Also fail when multicasting to wildcard address. */ + if (three || strchr(two, '*')) + two = NULL; + else + three = two; + } + new->next = daemon->relay6; + daemon->relay6 = new; + } #endif - else + + new->interface = opt_string_alloc(three); + } + + if (!two) { free(new->interface); ret_err_free(_("Bad dhcp-relay"), new); diff --git a/src/dnsmasq/pattern.c b/src/dnsmasq/pattern.c index 03e23b9a..928d2593 100644 --- a/src/dnsmasq/pattern.c +++ b/src/dnsmasq/pattern.c @@ -129,9 +129,9 @@ int is_valid_dns_name(const char *value) size_t num_bytes = 0; size_t num_labels = 0; - const char *label = NULL; + const char *c, *label = NULL; int is_label_numeric = 1; - for (const char *c = value;; c++) + for (c = value;; c++) { if (*c && *c != '-' && *c != '.' && @@ -242,11 +242,11 @@ int is_valid_dns_name_pattern(const char *value) size_t num_bytes = 0; size_t num_labels = 0; - const char *label = NULL; + const char *c, *label = NULL; int is_label_numeric = 1; size_t num_wildcards = 0; int previous_label_has_wildcard = 1; - for (const char *c = value;; c++) + for (c = value;; c++) { if (*c && *c != '*' && /* Wildcard. */ diff --git a/src/dnsmasq/rfc3315.c b/src/dnsmasq/rfc3315.c index 5c2ff97b..236df47b 100644 --- a/src/dnsmasq/rfc3315.c +++ b/src/dnsmasq/rfc3315.c @@ -2170,7 +2170,10 @@ void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, if (!relay->interface || strchr(relay->interface, '*') || (multicast_iface = if_nametoindex(relay->interface)) == 0 || setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_iface, sizeof(multicast_iface)) == -1) - my_syslog(MS_DHCP | LOG_ERR, _("Cannot multicast to DHCPv6 server without correct interface")); + { + my_syslog(MS_DHCP | LOG_ERR, _("Cannot multicast DHCP relay via interface %s"), relay->interface); + return; + } } send_from(daemon->dhcp6fd, 0, daemon->outpacket.iov_base, save_counter(-1), &to, &from, 0); @@ -2178,8 +2181,11 @@ void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, if (option_bool(OPT_LOG_OPTS)) { inet_ntop(AF_INET6, &relay->local, daemon->addrbuff, ADDRSTRLEN); - inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN); - my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, daemon->namebuff); + if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast)) + snprintf(daemon->namebuff, MAXDNAME, _("multicast via %s"), relay->interface); + else + inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN); + my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->namebuff); } /* Save this for replies */ @@ -2188,7 +2194,7 @@ void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, } } -unsigned short relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface) +int relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface) { struct dhcp_relay *relay; struct in6_addr link; @@ -2220,11 +2226,76 @@ unsigned short relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival put_opt6(opt6_ptr(opt, 0), opt6_len(opt)); memcpy(&peer->sin6_addr, &inbuff[18], IN6ADDRSZ); peer->sin6_scope_id = relay->iface_index; - return encap_type == DHCP6RELAYREPL ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT; - } - } + if (encap_type == DHCP6RELAYREPL) + { + peer->sin6_port = ntohs(DHCPV6_SERVER_PORT); + return 1; + } + + peer->sin6_port = ntohs(DHCPV6_CLIENT_PORT); + +#ifdef HAVE_SCRIPT + if (daemon->lease_change_command && encap_type == DHCP6REPLY) + { + /* decapsulate relayed message */ + opts = opt6_ptr(opt, 4); + end = opt6_ptr(opt, opt6_len(opt)); + + for (opt = opts; opt; opt = opt6_next(opt, end)) + if (opt6_type(opt) == OPTION6_IA_PD && opt6_len(opt) > 12) + { + void *ia_opts = opt6_ptr(opt, 12); + void *ia_end = opt6_ptr(opt, opt6_len(opt)); + void *ia_opt; + + for (ia_opt = ia_opts; ia_opt; ia_opt = opt6_next(ia_opt, ia_end)) + /* valid lifetime must not be zero. */ + if (opt6_type(ia_opt) == OPTION6_IAPREFIX && opt6_len(ia_opt) >= 25 && opt6_uint(ia_opt, 4, 4) != 0) + { + if (daemon->free_snoops || + (daemon->free_snoops = whine_malloc(sizeof(struct snoop_record)))) + { + struct snoop_record *snoop = daemon->free_snoops; + + daemon->free_snoops = snoop->next; + snoop->client = peer->sin6_addr; + snoop->prefix_len = opt6_uint(ia_opt, 8, 1); + memcpy(&snoop->prefix, opt6_ptr(ia_opt, 9), IN6ADDRSZ); + snoop->next = relay->snoop_records; + relay->snoop_records = snoop; + } + } + } + } +#endif + return 1; + } + + } + return 0; } +#ifdef HAVE_SCRIPT +int do_snoop_script_run(void) +{ + struct dhcp_relay *relay; + struct snoop_record *snoop; + + for (relay = daemon->relay6; relay; relay = relay->next) + if ((snoop = relay->snoop_records)) + { + relay->snoop_records = snoop->next; + snoop->next = daemon->free_snoops; + daemon->free_snoops = snoop; + + queue_relay_snoop(&snoop->client, relay->iface_index, &snoop->prefix, snoop->prefix_len); + return 1; + } + + return 0; +} +#endif + #endif diff --git a/src/dnsmasq/tftp.c b/src/dnsmasq/tftp.c index 3d87523c..e8474d9a 100644 --- a/src/dnsmasq/tftp.c +++ b/src/dnsmasq/tftp.c @@ -19,9 +19,9 @@ #ifdef HAVE_TFTP static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len); -static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix); +static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client); static void free_transfer(struct tftp_transfer *transfer); -static ssize_t tftp_err(int err, char *packet, char *message, char *file); +static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *arg2); static ssize_t tftp_err_oops(char *packet, const char *file); static ssize_t get_block(char *packet, struct tftp_transfer *transfer); static char *next(char **p, char *end); @@ -362,7 +362,7 @@ void tftp_request(struct listener *listen, time_t now) !(mode = next(&p, end)) || (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0)) { - len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff); + len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff, NULL); is_err = 1; } else @@ -472,7 +472,7 @@ void tftp_request(struct listener *listen, time_t now) strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff)); /* check permissions and open file */ - if ((transfer->file = check_tftp_fileperm(&len, prefix))) + if ((transfer->file = check_tftp_fileperm(&len, prefix, daemon->addrbuff))) { if ((len = get_block(packet, transfer)) == -1) len = tftp_err_oops(packet, daemon->namebuff); @@ -492,7 +492,7 @@ void tftp_request(struct listener *listen, time_t now) } } -static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix) +static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client) { char *packet = daemon->packet, *namebuff = daemon->namebuff; struct tftp_file *file; @@ -509,7 +509,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix) { if (errno == ENOENT) { - *len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff); + *len = tftp_err(ERR_FNF, packet, _("file %s not found for %s"), namebuff, client); return NULL; } else if (errno == EACCES) @@ -562,8 +562,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix) return file; perm: - errno = EACCES; - *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff); + *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff, strerror(EACCES)); if (fd != -1) close(fd); return NULL; @@ -599,7 +598,7 @@ void check_tftp_listeners(time_t now) { /* Wrong source address. See rfc1350 para 4. */ prettyprint_addr(&peer, daemon->addrbuff); - len = tftp_err(ERR_TID, daemon->packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff); + len = tftp_err(ERR_TID, daemon->packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff, NULL); while(retry_send(sendto(transfer->sockfd, daemon->packet, len, 0, &peer.sa, sa_len(&peer)))); } } @@ -743,22 +742,21 @@ static void sanitise(char *buf) } #define MAXMESSAGE 500 /* limit to make packet < 512 bytes and definitely smaller than buffer */ -static ssize_t tftp_err(int err, char *packet, char *message, char *file) +static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *arg2) { struct errmess { unsigned short op, err; char message[]; } *mess = (struct errmess *)packet; ssize_t len, ret = 4; - char *errstr = strerror(errno); - + memset(packet, 0, daemon->packet_buff_sz); if (file) sanitise(file); mess->op = htons(OP_ERR); mess->err = htons(err); - len = snprintf(mess->message, MAXMESSAGE, message, file, errstr); + len = snprintf(mess->message, MAXMESSAGE, message, file, arg2); ret += (len < MAXMESSAGE) ? len + 1 : MAXMESSAGE; /* include terminating zero */ if (err != ERR_FNF || !option_bool(OPT_QUIET_TFTP)) @@ -772,7 +770,7 @@ static ssize_t tftp_err_oops(char *packet, const char *file) /* May have >1 refs to file, so potentially mangle a copy of the name */ if (file != daemon->namebuff) strcpy(daemon->namebuff, file); - return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff); + return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff, strerror(errno)); } /* return -1 for error, zero for done. */ diff --git a/src/main.c b/src/main.c index 748d70eb..0253a56b 100644 --- a/src/main.c +++ b/src/main.c @@ -98,9 +98,9 @@ int main (int argc, char* argv[]) log_counter_info(); check_setupVarsconf(); - // Check for availability of advanced capabilities - // immediately before starting the resolver. - check_capabilities(); + // Check for availability of capabilities in debug mode + if(config.debug & DEBUG_CAPS) + check_capabilities(); // Start the resolver startup = false; diff --git a/test/test_suite.bats b/test/test_suite.bats index 3c82e1bd..5de5bb00 100644 --- a/test/test_suite.bats +++ b/test/test_suite.bats @@ -11,6 +11,13 @@ [[ ${lines[6]} == "" ]] } +@test "DNS server port is reported" { + run bash -c 'echo ">dns-port >quit" | nc -v 127.0.0.1 4711' + printf "%s\n" "${lines[@]}" + [[ ${lines[1]} == "53" ]] + [[ ${lines[2]} == "" ]] +} + @test "Running a second instance is detected and prevented" { run bash -c 'su pihole -s /bin/sh -c "/home/pihole/pihole-FTL -f"' printf "%s\n" "${lines[@]}"