From 5a095b1cb15b729aebb6ab280f44b217f1e11810 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 27 Jul 2022 19:20:03 +0200 Subject: [PATCH 1/2] Ensure errno value is restored on return of syscall functions. Signed-off-by: DL6ER --- src/api/api.c | 360 ++++++++++++++--------------- src/api/api.h | 40 ++-- src/api/request.c | 22 +- src/api/request.h | 2 +- src/api/socket.c | 367 ++++++++++++++---------------- src/api/socket.h | 5 +- src/daemon.c | 6 +- src/dnsmasq_interface.c | 13 +- src/dnsmasq_interface.h | 1 - src/syscalls/accept.c | 12 +- src/syscalls/calloc.c | 7 + src/syscalls/fopen.c | 7 + src/syscalls/ftlallocate.c | 1 + src/syscalls/pthread_mutex_lock.c | 12 +- src/syscalls/realloc.c | 6 + src/syscalls/recv.c | 12 +- src/syscalls/recvfrom.c | 12 +- src/syscalls/select.c | 12 +- src/syscalls/sendto.c | 12 +- src/syscalls/vasprintf.c | 3 + src/syscalls/vfprintf.c | 9 + src/syscalls/vsnprintf.c | 4 + src/syscalls/vsprintf.c | 3 + src/syscalls/write.c | 23 +- 24 files changed, 493 insertions(+), 458 deletions(-) diff --git a/src/api/api.c b/src/api/api.c index 0857b5f68..32047a2f0 100644 --- a/src/api/api.c +++ b/src/api/api.c @@ -76,7 +76,7 @@ static int __attribute__((pure)) cmpdesc(const void *a, const void *b) return 0; } -void getStats(const int *sock) +void getStats(const int sock) { const int blocked = blocked_queries(); const int total = counters->queries; @@ -87,11 +87,11 @@ void getStats(const int *sock) percentage = 1e2f*blocked/total; // Send domains being blocked - if(istelnet[*sock]) { - ssend(*sock, "domains_being_blocked %i\n", counters->gravity); + if(istelnet[sock]) { + ssend(sock, "domains_being_blocked %i\n", counters->gravity); } else - pack_int32(*sock, counters->gravity); + pack_int32(sock, counters->gravity); // unique_clients: count only clients that have been active within the most recent 24 hours int activeclients = 0; @@ -106,13 +106,13 @@ void getStats(const int *sock) activeclients++; } - if(istelnet[*sock]) { - ssend(*sock, "dns_queries_today %i\nads_blocked_today %i\nads_percentage_today %f\n", + if(istelnet[sock]) { + ssend(sock, "dns_queries_today %i\nads_blocked_today %i\nads_percentage_today %f\n", total, blocked, percentage); - ssend(*sock, "unique_domains %i\nqueries_forwarded %i\nqueries_cached %i\n", + ssend(sock, "unique_domains %i\nqueries_forwarded %i\nqueries_cached %i\n", counters->domains, forwarded_queries(), cached_queries()); - ssend(*sock, "clients_ever_seen %i\n", counters->clients); - ssend(*sock, "unique_clients %i\n", activeclients); + ssend(sock, "clients_ever_seen %i\n", counters->clients); + ssend(sock, "unique_clients %i\n", activeclients); // Sum up all query types (A, AAAA, ANY, SRV, SOA, ...) int sumalltypes = 0; @@ -120,45 +120,45 @@ void getStats(const int *sock) { sumalltypes += counters->querytype[queryType]; } - ssend(*sock, "dns_queries_all_types %i\n", sumalltypes); + ssend(sock, "dns_queries_all_types %i\n", sumalltypes); // Send individual reply type counters int sumallreplies = 0; for(enum reply_type reply = REPLY_UNKNOWN; reply < QUERY_REPLY_MAX; reply++) { - ssend(*sock, "reply_%s %i\n", get_query_reply_str(reply), counters->reply[reply]); + ssend(sock, "reply_%s %i\n", get_query_reply_str(reply), counters->reply[reply]); sumallreplies += counters->reply[reply]; } - ssend(*sock, "dns_queries_all_replies %i\n", sumallreplies); - ssend(*sock, "privacy_level %i\n", config.privacylevel); + ssend(sock, "dns_queries_all_replies %i\n", sumallreplies); + ssend(sock, "privacy_level %i\n", config.privacylevel); } else { - pack_int32(*sock, total); - pack_int32(*sock, blocked); - pack_float(*sock, percentage); - pack_int32(*sock, counters->domains); - pack_int32(*sock, forwarded_queries()); - pack_int32(*sock, cached_queries()); - pack_int32(*sock, counters->clients); - pack_int32(*sock, activeclients); + pack_int32(sock, total); + pack_int32(sock, blocked); + pack_float(sock, percentage); + pack_int32(sock, counters->domains); + pack_int32(sock, forwarded_queries()); + pack_int32(sock, cached_queries()); + pack_int32(sock, counters->clients); + pack_int32(sock, activeclients); } // Send status - if(istelnet[*sock]) { - ssend(*sock, "status %s\n", blockingstatus ? "enabled" : "disabled"); + if(istelnet[sock]) { + ssend(sock, "status %s\n", blockingstatus ? "enabled" : "disabled"); } else - pack_uint8(*sock, blockingstatus); + pack_uint8(sock, blockingstatus); } -void getOverTime(const int *sock) +void getOverTime(const int sock) { - if(istelnet[*sock]) + if(istelnet[sock]) { for(int slot = 0; slot < OVERTIME_SLOTS; slot++) { - ssend(*sock,"%lli %i %i\n", + ssend(sock,"%lli %i %i\n", (long long)overTime[slot].timestamp, overTime[slot].total, overTime[slot].blocked); @@ -170,22 +170,22 @@ void getOverTime(const int *sock) // and map16 can hold up to (2^16)-1 = 65535 pairs // Send domains over time - pack_map16_start(*sock, (uint16_t) OVERTIME_SLOTS); + pack_map16_start(sock, (uint16_t) OVERTIME_SLOTS); for(int slot = 0; slot < OVERTIME_SLOTS; slot++) { - pack_int32(*sock, (int32_t)overTime[slot].timestamp); - pack_int32(*sock, overTime[slot].total); + pack_int32(sock, (int32_t)overTime[slot].timestamp); + pack_int32(sock, overTime[slot].total); } // Send ads over time - pack_map16_start(*sock, (uint16_t) OVERTIME_SLOTS); + pack_map16_start(sock, (uint16_t) OVERTIME_SLOTS); for(int slot = 0; slot < OVERTIME_SLOTS; slot++) { - pack_int32(*sock, (int32_t)overTime[slot].timestamp); - pack_int32(*sock, overTime[slot].blocked); + pack_int32(sock, (int32_t)overTime[slot].timestamp); + pack_int32(sock, overTime[slot].blocked); } } } -void getTopDomains(const char *client_message, const int *sock) +void getTopDomains(const char *client_message, const int sock) { int temparray[counters->domains][2], count=10, num; bool audit = false, asc = false; @@ -196,8 +196,8 @@ void getTopDomains(const char *client_message, const int *sock) get_privacy_level(NULL); if(config.privacylevel >= PRIVACY_HIDE_DOMAINS) { // Always send the total number of domains, but pretend it's 0 - if(!istelnet[*sock]) - pack_int32(*sock, 0); + if(!istelnet[sock]) + pack_int32(sock, 0); return; } @@ -269,13 +269,13 @@ void getTopDomains(const char *client_message, const int *sock) } } - if(!istelnet[*sock]) + if(!istelnet[sock]) { // Send the data required to get the percentage each domain has been blocked / queried if(blocked) - pack_int32(*sock, blocked_queries()); + pack_int32(sock, blocked_queries()); else - pack_int32(*sock, counters->queries); + pack_int32(sock, counters->queries); } int n = 0; @@ -306,26 +306,26 @@ void getTopDomains(const char *client_message, const int *sock) if(blocked && showblocked && domain->blockedcount > 0) { - if(istelnet[*sock]) - ssend(*sock, "%i %i %s\n", n, domain->blockedcount, getstr(domain->domainpos)); + if(istelnet[sock]) + ssend(sock, "%i %i %s\n", n, domain->blockedcount, getstr(domain->domainpos)); else { - if(!pack_str32(*sock, getstr(domain->domainpos))) + if(!pack_str32(sock, getstr(domain->domainpos))) return; - pack_int32(*sock, domain->blockedcount); + pack_int32(sock, domain->blockedcount); } n++; } else if(!blocked && showpermitted && (domain->count - domain->blockedcount) > 0) { - if(istelnet[*sock]) - ssend(*sock,"%i %i %s\n",n,(domain->count - domain->blockedcount),getstr(domain->domainpos)); + if(istelnet[sock]) + ssend(sock,"%i %i %s\n",n,(domain->count - domain->blockedcount),getstr(domain->domainpos)); else { - if(!pack_str32(*sock, getstr(domain->domainpos))) + if(!pack_str32(sock, getstr(domain->domainpos))) return; - pack_int32(*sock, domain->count - domain->blockedcount); + pack_int32(sock, domain->count - domain->blockedcount); } n++; } @@ -339,7 +339,7 @@ void getTopDomains(const char *client_message, const int *sock) clearSetupVarsArray(); } -void getTopClients(const char *client_message, const int *sock) +void getTopClients(const char *client_message, const int sock) { int temparray[counters->clients][2], count=10, num; @@ -347,8 +347,8 @@ void getTopClients(const char *client_message, const int *sock) get_privacy_level(NULL); if(config.privacylevel >= PRIVACY_HIDE_DOMAINS_CLIENTS) { // Always send the total number of clients, but pretend it's 0 - if(!istelnet[*sock]) - pack_int32(*sock, 0); + if(!istelnet[sock]) + pack_int32(sock, 0); return; } @@ -408,10 +408,10 @@ void getTopClients(const char *client_message, const int *sock) getSetupVarsArray(excludeclients); } - if(!istelnet[*sock]) + if(!istelnet[sock]) { // Send the total queries so they can make percentages from this data - pack_int32(*sock, counters->queries); + pack_int32(sock, counters->queries); } int n = 0; @@ -450,14 +450,14 @@ void getTopClients(const char *client_message, const int *sock) // - the client made at least one query within the most recent 24 hours if(includezeroclients || ccount > 0) { - if(istelnet[*sock]) - ssend(*sock,"%i %i %s %s\n", n, ccount, client_ip, client_name); + if(istelnet[sock]) + ssend(sock,"%i %i %s %s\n", n, ccount, client_ip, client_name); else { - if(!pack_str32(*sock, "") || !pack_str32(*sock, client_ip)) + if(!pack_str32(sock, "") || !pack_str32(sock, client_ip)) return; - pack_int32(*sock, ccount); + pack_int32(sock, ccount); } n++; } @@ -471,7 +471,7 @@ void getTopClients(const char *client_message, const int *sock) } -void getUpstreamDestinations(const char *client_message, const int *sock) +void getUpstreamDestinations(const char *client_message, const int sock) { bool sort = true; int temparray[counters->upstreams][2], sumforwarded = 0; @@ -575,24 +575,24 @@ void getUpstreamDestinations(const char *client_message, const int *sock) // - only if percentage > 0.0 for all others (i > 0) if(percentage > 0.0f || i < 0) { - if(istelnet[*sock]) + if(istelnet[sock]) if(upstream_port != 0) - ssend(*sock, "%i %.2f %s#%u %s#%u\n", i, percentage, + ssend(sock, "%i %.2f %s#%u %s#%u\n", i, percentage, ip, upstream_port, name, upstream_port); else - ssend(*sock, "%i %.2f %s %s\n", i, percentage, ip, name); + ssend(sock, "%i %.2f %s %s\n", i, percentage, ip, name); else { - if(!pack_str32(*sock, name) || !pack_str32(*sock, ip)) + if(!pack_str32(sock, name) || !pack_str32(sock, ip)) return; - pack_float(*sock, percentage); + pack_float(sock, percentage); } } } } -void getQueryTypes(const int *sock) +void getQueryTypes(const int sock) { int total = 0; for(enum query_types type = TYPE_A; type < TYPE_MAX; type++) @@ -611,8 +611,8 @@ void getQueryTypes(const int *sock) } } - if(istelnet[*sock]) { - ssend(*sock, "A (IPv4): %.2f\nAAAA (IPv6): %.2f\nANY: %.2f\nSRV: %.2f\n" + if(istelnet[sock]) { + ssend(sock, "A (IPv4): %.2f\nAAAA (IPv6): %.2f\nANY: %.2f\nSRV: %.2f\n" "SOA: %.2f\nPTR: %.2f\nTXT: %.2f\nNAPTR: %.2f\n" "MX: %.2f\nDS: %.2f\nRRSIG: %.2f\nDNSKEY: %.2f\n" "NS: %.2f\n" "OTHER: %.2f\n\nSVCB: %.2f\nHTTPS: %.2f\n", @@ -622,42 +622,42 @@ void getQueryTypes(const int *sock) percentage[TYPE_NS], percentage[TYPE_OTHER], percentage[TYPE_SVCB], percentage[TYPE_HTTPS]); } else { - pack_str32(*sock, "A (IPv4)"); - pack_float(*sock, percentage[TYPE_A]); - pack_str32(*sock, "AAAA (IPv6)"); - pack_float(*sock, percentage[TYPE_AAAA]); - pack_str32(*sock, "ANY"); - pack_float(*sock, percentage[TYPE_ANY]); - pack_str32(*sock, "SRV"); - pack_float(*sock, percentage[TYPE_SRV]); - pack_str32(*sock, "SOA"); - pack_float(*sock, percentage[TYPE_SOA]); - pack_str32(*sock, "PTR"); - pack_float(*sock, percentage[TYPE_PTR]); - pack_str32(*sock, "TXT"); - pack_float(*sock, percentage[TYPE_TXT]); - pack_str32(*sock, "NAPTR"); - pack_float(*sock, percentage[TYPE_NAPTR]); - pack_str32(*sock, "MX"); - pack_float(*sock, percentage[TYPE_MX]); - pack_str32(*sock, "DS"); - pack_float(*sock, percentage[TYPE_DS]); - pack_str32(*sock, "RRSIG"); - pack_float(*sock, percentage[TYPE_RRSIG]); - pack_str32(*sock, "DNSKEY"); - pack_float(*sock, percentage[TYPE_DNSKEY]); - pack_str32(*sock, "NS"); - pack_float(*sock, percentage[TYPE_NS]); - pack_str32(*sock, "OTHER"); - pack_float(*sock, percentage[TYPE_OTHER]); - pack_str32(*sock, "SVCB"); - pack_float(*sock, percentage[TYPE_SVCB]); - pack_str32(*sock, "HTTPS"); - pack_float(*sock, percentage[TYPE_HTTPS]); + pack_str32(sock, "A (IPv4)"); + pack_float(sock, percentage[TYPE_A]); + pack_str32(sock, "AAAA (IPv6)"); + pack_float(sock, percentage[TYPE_AAAA]); + pack_str32(sock, "ANY"); + pack_float(sock, percentage[TYPE_ANY]); + pack_str32(sock, "SRV"); + pack_float(sock, percentage[TYPE_SRV]); + pack_str32(sock, "SOA"); + pack_float(sock, percentage[TYPE_SOA]); + pack_str32(sock, "PTR"); + pack_float(sock, percentage[TYPE_PTR]); + pack_str32(sock, "TXT"); + pack_float(sock, percentage[TYPE_TXT]); + pack_str32(sock, "NAPTR"); + pack_float(sock, percentage[TYPE_NAPTR]); + pack_str32(sock, "MX"); + pack_float(sock, percentage[TYPE_MX]); + pack_str32(sock, "DS"); + pack_float(sock, percentage[TYPE_DS]); + pack_str32(sock, "RRSIG"); + pack_float(sock, percentage[TYPE_RRSIG]); + pack_str32(sock, "DNSKEY"); + pack_float(sock, percentage[TYPE_DNSKEY]); + pack_str32(sock, "NS"); + pack_float(sock, percentage[TYPE_NS]); + pack_str32(sock, "OTHER"); + pack_float(sock, percentage[TYPE_OTHER]); + pack_str32(sock, "SVCB"); + pack_float(sock, percentage[TYPE_SVCB]); + pack_str32(sock, "HTTPS"); + pack_float(sock, percentage[TYPE_HTTPS]); } } -void getAllQueries(const char *client_message, const int *sock) +void getAllQueries(const char *client_message, const int sock) { // Exit before processing any data if requested via config setting get_privacy_level(NULL); @@ -1053,9 +1053,9 @@ void getAllQueries(const char *client_message, const int *sock) delay = 0UL; } - if(istelnet[*sock]) + if(istelnet[sock]) { - ssend(*sock,"%lli %s %s %s %i %i %i %lu %s %i %s#%u \"%s\"", + ssend(sock,"%lli %s %s %s %i %i %i %lu %s %i %s#%u \"%s\"", (long long)query->timestamp, qtype, domain, @@ -1071,23 +1071,23 @@ void getAllQueries(const char *client_message, const int *sock) query->ede == -1 ? "" : get_edestr(query->ede)); if(config.debug & DEBUG_API) - ssend(*sock, " \"%i\"", queryID); - ssend(*sock, "\n"); + ssend(sock, " \"%i\"", queryID); + ssend(sock, "\n"); } else { - pack_int32(*sock, (int32_t)query->timestamp); + pack_int32(sock, (int32_t)query->timestamp); // Use a fixstr because the length of qtype is always 4 (max is 31 for fixstr) - if(!pack_fixstr(*sock, qtype)) + if(!pack_fixstr(sock, qtype)) break; // Use str32 for domain and client because we have no idea how long they will be (max is 4294967295 for str32) - if(!pack_str32(*sock, domain) || !pack_str32(*sock, clientIPName)) + if(!pack_str32(sock, domain) || !pack_str32(sock, clientIPName)) break; - pack_uint8(*sock, query->status); - pack_uint8(*sock, query->dnssec); + pack_uint8(sock, query->status); + pack_uint8(sock, query->dnssec); } } @@ -1105,7 +1105,7 @@ void getAllQueries(const char *client_message, const int *sock) free(clientid_list); } -void getRecentBlocked(const char *client_message, const int *sock) +void getRecentBlocked(const char *client_message, const int sock) { int num=1; @@ -1132,9 +1132,9 @@ void getRecentBlocked(const char *client_message, const int *sock) if(domain == NULL) continue; - if(istelnet[*sock]) - ssend(*sock,"%s\n", domain); - else if(!pack_str32(*sock, domain)) + if(istelnet[sock]) + ssend(sock,"%s\n", domain); + else if(!pack_str32(sock, domain)) return; // Only count when sent successfully @@ -1146,15 +1146,15 @@ void getRecentBlocked(const char *client_message, const int *sock) } } -void getClientID(const int *sock) +void getClientID(const int sock) { - if(istelnet[*sock]) - ssend(*sock,"%i\n", *sock); + if(istelnet[sock]) + ssend(sock,"%i\n", sock); else - pack_int32(*sock, *sock); + pack_int32(sock, sock); } -void getVersion(const int *sock) +void getVersion(const int sock) { const char *commit = GIT_HASH; const char *tag = GIT_TAG; @@ -1165,25 +1165,25 @@ void getVersion(const int *sock) memcpy(hash, commit, min((size_t)7, strlen(commit))); if(strlen(tag) > 1) { - if(istelnet[*sock]) + if(istelnet[sock]) ssend( - *sock, + sock, "version %s\ntag %s\nbranch %s\nhash %s\ndate %s\n", version, tag, GIT_BRANCH, hash, GIT_DATE ); else { - if(!pack_str32(*sock, version) || - !pack_str32(*sock, (char *) tag) || - !pack_str32(*sock, GIT_BRANCH) || - !pack_str32(*sock, hash) || - !pack_str32(*sock, GIT_DATE)) + if(!pack_str32(sock, version) || + !pack_str32(sock, (char *) tag) || + !pack_str32(sock, GIT_BRANCH) || + !pack_str32(sock, hash) || + !pack_str32(sock, GIT_DATE)) return; } } else { - if(istelnet[*sock]) + if(istelnet[sock]) ssend( - *sock, + sock, "version vDev-%s\ntag %s\nbranch %s\nhash %s\ndate %s\n", hash, tag, GIT_BRANCH, hash, GIT_DATE ); @@ -1192,11 +1192,11 @@ void getVersion(const int *sock) if(hashVersion == NULL) return; sprintf(hashVersion, "vDev-%s", hash); - if(!pack_str32(*sock, hashVersion) || - !pack_str32(*sock, (char *) tag) || - !pack_str32(*sock, GIT_BRANCH) || - !pack_str32(*sock, hash) || - !pack_str32(*sock, GIT_DATE)) + if(!pack_str32(sock, hashVersion) || + !pack_str32(sock, (char *) tag) || + !pack_str32(sock, GIT_BRANCH) || + !pack_str32(sock, hash) || + !pack_str32(sock, GIT_DATE)) return; free(hashVersion); @@ -1204,7 +1204,7 @@ void getVersion(const int *sock) } } -void getDBstats(const int *sock) +void getDBstats(const int sock) { // Get file details unsigned long long int filesize = get_FTL_db_filesize(); @@ -1213,19 +1213,19 @@ void getDBstats(const int *sock) double formatted = 0.0; format_memory_size(prefix, filesize, &formatted); - if(istelnet[*sock]) - ssend(*sock, "queries in database: %i\ndatabase filesize: %.2f %sB\nSQLite version: %s\n", + if(istelnet[sock]) + ssend(sock, "queries in database: %i\ndatabase filesize: %.2f %sB\nSQLite version: %s\n", get_number_of_queries_in_DB(NULL), formatted, prefix, get_sqlite3_version()); else { - pack_int32(*sock, get_number_of_queries_in_DB(NULL)); - pack_int64(*sock, filesize); + pack_int32(sock, get_number_of_queries_in_DB(NULL)); + pack_int64(sock, filesize); - if(!pack_str32(*sock, (char *) get_sqlite3_version())) + if(!pack_str32(sock, (char *) get_sqlite3_version())) return; } } -void getClientsOverTime(const int *sock) +void getClientsOverTime(const int sock) { // Exit before processing any data if requested via config setting get_privacy_level(NULL); @@ -1263,10 +1263,10 @@ void getClientsOverTime(const int *sock) // Main return loop for(int slot = 0; slot < OVERTIME_SLOTS; slot++) { - if(istelnet[*sock]) - ssend(*sock, "%lli", (long long)overTime[slot].timestamp); + if(istelnet[sock]) + ssend(sock, "%lli", (long long)overTime[slot].timestamp); else - pack_int32(*sock, (int32_t)overTime[slot].timestamp); + pack_int32(sock, (int32_t)overTime[slot].timestamp); // Loop over forward destinations to generate output to be sent to the client for(int clientID = 0; clientID < counters->clients; clientID++) @@ -1284,23 +1284,23 @@ void getClientsOverTime(const int *sock) continue; const int thisclient = client->overTime[slot]; - if(istelnet[*sock]) - ssend(*sock, " %i", thisclient); + if(istelnet[sock]) + ssend(sock, " %i", thisclient); else - pack_int32(*sock, thisclient); + pack_int32(sock, thisclient); } - if(istelnet[*sock]) - ssend(*sock, "\n"); + if(istelnet[sock]) + ssend(sock, "\n"); else - pack_int32(*sock, -1); + pack_int32(sock, -1); } if(excludeclients != NULL) clearSetupVarsArray(); } -void getClientNames(const int *sock) +void getClientNames(const int sock) { // Exit before processing any data if requested via config setting get_privacy_level(NULL); @@ -1353,11 +1353,11 @@ void getClientNames(const int *sock) const char *client_ip = getstr(client->ippos); const char *client_name = getstr(client->namepos); - if(istelnet[*sock]) - ssend(*sock, "%s %s\n", client_name, client_ip); + if(istelnet[sock]) + ssend(sock, "%s %s\n", client_name, client_ip); else { - pack_str32(*sock, client_name); - pack_str32(*sock, client_ip); + pack_str32(sock, client_name); + pack_str32(sock, client_ip); } } @@ -1365,7 +1365,7 @@ void getClientNames(const int *sock) clearSetupVarsArray(); } -void getUnknownQueries(const int *sock) +void getUnknownQueries(const int sock) { // Exit before processing any data if requested via config setting get_privacy_level(NULL); @@ -1401,22 +1401,22 @@ void getUnknownQueries(const int *sock) // Get client IP string const char *clientIP = getstr(client->ippos); - if(istelnet[*sock]) - ssend(*sock, "%lli %i %i %s %s %s %i %s\n", (long long)query->timestamp, queryID, query->id, type, getstr(domain->domainpos), clientIP, query->status, query->flags.complete ? "true" : "false"); + if(istelnet[sock]) + ssend(sock, "%lli %i %i %s %s %s %i %s\n", (long long)query->timestamp, queryID, query->id, type, getstr(domain->domainpos), clientIP, query->status, query->flags.complete ? "true" : "false"); else { - pack_int32(*sock, (int32_t)query->timestamp); - pack_int32(*sock, query->id); + pack_int32(sock, (int32_t)query->timestamp); + pack_int32(sock, query->id); // Use a fixstr because the length of qtype is always 4 (max is 31 for fixstr) - if(!pack_fixstr(*sock, type)) + if(!pack_fixstr(sock, type)) return; // Use str32 for domain and client because we have no idea how long they will be (max is 4294967295 for str32) - if(!pack_str32(*sock, getstr(domain->domainpos)) || !pack_str32(*sock, clientIP)) + if(!pack_str32(sock, getstr(domain->domainpos)) || !pack_str32(sock, clientIP)) return; - pack_uint8(*sock, query->status); - pack_bool(*sock, query->flags.complete); + pack_uint8(sock, query->status); + pack_bool(sock, query->flags.complete); } } } @@ -1424,12 +1424,12 @@ void getUnknownQueries(const int *sock) // FTL_unlink_DHCP_lease() extern bool FTL_unlink_DHCP_lease(const char *ipaddr); -void delete_lease(const char *client_message, const int *sock) +void delete_lease(const char *client_message, const int sock) { // Extract IP address from request char ipaddr[INET6_ADDRSTRLEN] = { 0 }; if(sscanf(client_message, ">delete-lease %"xstr(INET6_ADDRSTRLEN)"s", ipaddr) < 1) { - ssend(*sock, "ERROR: No IP address specified!\n"); + ssend(sock, "ERROR: No IP address specified!\n"); return; } ipaddr[sizeof(ipaddr) - 1] = '\0'; @@ -1438,24 +1438,24 @@ void delete_lease(const char *client_message, const int *sock) logg("Received request to delete lease for %s", ipaddr); if(FTL_unlink_DHCP_lease(ipaddr)) - ssend(*sock, "OK: Removed specified lease\n"); + ssend(sock, "OK: Removed specified lease\n"); else - ssend(*sock, "ERROR: Specified IP address invalid!\n"); + ssend(sock, "ERROR: Specified IP address invalid!\n"); if(config.debug & DEBUG_API) logg("...done"); } -void getDNSport(const int *sock) +void getDNSport(const int sock) { // Return DNS port used by FTL - ssend(*sock, "%d\n", config.dns_port); + ssend(sock, "%d\n", config.dns_port); } -void getMAXLOGAGE(const int *sock) +void getMAXLOGAGE(const int sock) { // Return maxlogage used by FTL - ssend(*sock, "%d\n", config.maxlogage); + ssend(sock, "%d\n", config.maxlogage); } static bool getDefaultInterface(char iface[IF_NAMESIZE], in_addr_t *gw) @@ -1506,13 +1506,13 @@ static bool getDefaultInterface(char iface[IF_NAMESIZE], in_addr_t *gw) return gw != 0; } -void getGateway(const int *sock) +void getGateway(const int sock) { in_addr_t gw = 0; char iface[IF_NAMESIZE] = { 0 }; getDefaultInterface(iface, &gw); - ssend(*sock, "%s %s\n", inet_ntoa(*(struct in_addr *) &gw), iface); + ssend(sock, "%s %s\n", inet_ntoa(*(struct in_addr *) &gw), iface); } struct if_info { @@ -1710,22 +1710,22 @@ static bool listInterfaces(struct if_info **head, char default_iface[IF_NAMESIZE return true; } -static void send_iface(const int *sock, struct if_info *iface) +static bool send_iface(const int sock, struct if_info *iface) { double tx = 0.0, rx = 0.0; char txp[2] = { 0 }, rxp[2] = { 0 }; format_memory_size(txp, iface->tx_bytes, &tx); format_memory_size(rxp, iface->rx_bytes, &rx); - ssend(*sock, "%s %s %i %.1f%sB %.1f%sB %s %s\n", - iface->name, - iface->carrier ? "UP" : "DOWN", - iface->speed, - tx, txp, rx, rxp, - iface->carrier && iface->ip.v4 ? iface->ip.v4 : "-", - iface->carrier && iface->ip.v6 ? iface->ip.v6 : "-"); + return ssend(sock, "%s %s %i %.1f%sB %.1f%sB %s %s\n", + iface->name, + iface->carrier ? "UP" : "DOWN", + iface->speed, + tx, txp, rx, rxp, + iface->carrier && iface->ip.v4 ? iface->ip.v4 : "-", + iface->carrier && iface->ip.v6 ? iface->ip.v6 : "-"); } -void getInterfaces(const int *sock) +void getInterfaces(const int sock) { // Get interface with default route in_addr_t gw = 0; @@ -1736,7 +1736,7 @@ void getInterfaces(const int *sock) struct if_info *ifinfo = NULL; if(!listInterfaces(&ifinfo, default_iface)) { - ssend(*sock, "ERROR"); + ssend(sock, "ERROR"); return; } diff --git a/src/api/api.h b/src/api/api.h index afa1a9350..5867859dc 100644 --- a/src/api/api.h +++ b/src/api/api.h @@ -11,29 +11,29 @@ #define API_H // Statistic methods -void getStats(const int *sock); -void getOverTime(const int *sock); -void getTopDomains(const char *client_message, const int *sock); -void getTopClients(const char *client_message, const int *sock); -void getUpstreamDestinations(const char *client_message, const int *sock); -void getQueryTypes(const int *sock); -void getAllQueries(const char *client_message, const int *sock); -void getRecentBlocked(const char *client_message, const int *sock); -void getClientsOverTime(const int *sock); -void getClientNames(const int *sock); +void getStats(const int sock); +void getOverTime(const int sock); +void getTopDomains(const char *client_message, const int sock); +void getTopClients(const char *client_message, const int sock); +void getUpstreamDestinations(const char *client_message, const int sock); +void getQueryTypes(const int sock); +void getAllQueries(const char *client_message, const int sock); +void getRecentBlocked(const char *client_message, const int sock); +void getClientsOverTime(const int sock); +void getClientNames(const int sock); // FTL methods -void getClientID(const int *sock); -void getVersion(const int *sock); -void getDBstats(const int *sock); -void getUnknownQueries(const int *sock); -void getMAXLOGAGE(const int *sock); -void getGateway(const int *sock); -void getInterfaces(const int *sock); +void getClientID(const int sock); +void getVersion(const int sock); +void getDBstats(const int sock); +void getUnknownQueries(const int sock); +void getMAXLOGAGE(const int sock); +void getGateway(const int sock); +void getInterfaces(const int sock); // DNS resolver methods (dnsmasq_interface.c) -void getCacheInformation(const int *sock); -void getDNSport(const int *sock); +void getCacheInformation(const int sock); +void getDNSport(const int sock); // MessagePack serialization helpers void pack_eom(const int sock); @@ -48,6 +48,6 @@ bool pack_str32(const int sock, const char *string); void pack_map16_start(const int sock, const uint16_t length); // DHCP lease management -void delete_lease(const char *client_message, const int *sock); +void delete_lease(const char *client_message, const int sock); #endif // API_H diff --git a/src/api/request.c b/src/api/request.c index 5ebc28cc7..88bb5a019 100644 --- a/src/api/request.c +++ b/src/api/request.c @@ -26,7 +26,7 @@ bool __attribute__((pure)) command(const char *client_message, const char* cmd) return strstr(client_message, cmd) != NULL; } -void process_request(const char *client_message, int *sock) +bool process_request(const char *client_message, const int sock) { char EOT[2]; EOT[0] = 0x04; @@ -191,21 +191,15 @@ void process_request(const char *client_message, int *sock) if(command(client_message, ">quit") || command(client_message, EOT)) { if(config.debug & DEBUG_API) - logg("Received >quit or EOT on socket %d", *sock); - processed = true; - close(*sock); - *sock = 0; + logg("Received >quit or EOT on socket %d", sock); + return true; } if(!processed) - { - ssend(*sock, "unknown command: %s\n", client_message); - } + ssend(sock, "unknown command: %s\n", client_message); - // End of queryable commands - if(*sock != 0) - { - // Send EOM - seom(*sock); - } + // End of queryable commands: Send EOM + seom(sock); + + return false; } diff --git a/src/api/request.h b/src/api/request.h index ab2baa0aa..5e57f5824 100644 --- a/src/api/request.h +++ b/src/api/request.h @@ -10,7 +10,7 @@ #ifndef REQUEST_H #define REQUEST_H -void process_request(const char *client_message, int *sock); +bool process_request(const char *client_message, const int sock); bool command(const char *client_message, const char* cmd) __attribute__((pure)); #endif //REQUEST_H diff --git a/src/api/socket.c b/src/api/socket.c index 6495aa146..c3d62f864 100644 --- a/src/api/socket.c +++ b/src/api/socket.c @@ -31,9 +31,7 @@ #define BACKLOG 5 // File descriptors -int socketfd = 0, telnetfd4 = 0, telnetfd6 = 0; bool dualstack = false; -bool ipv4telnet = false, ipv6telnet = false, sock_avail = false; bool istelnet[MAXCONNS]; void saveport(int port) @@ -59,12 +57,11 @@ void saveport(int port) } } -static bool bind_to_telnet_port_IPv4(int *socketdescriptor) +static int bind_to_telnet_port_IPv4(void) { // IPv4 socket - *socketdescriptor = socket(AF_INET, SOCK_STREAM, 0); - - if(*socketdescriptor < 0) + const int socketdescriptor = socket(AF_INET, SOCK_STREAM, 0); + if(socketdescriptor < 0) { logg("Error opening IPv4 telnet socket: %s (%i)", strerror(errno), errno); exit(EXIT_FAILURE); @@ -77,7 +74,7 @@ static bool bind_to_telnet_port_IPv4(int *socketdescriptor) // new instance will fail if there were connections open to the previous // instance when you killed it. Those connections will hold the TCP port in // the TIME_WAIT state for 30-120 seconds, so you fall into case 1 above. - if(setsockopt(*socketdescriptor, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) != 0) + if(setsockopt(socketdescriptor, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) != 0) logg("WARN: allowing re-binding (IPv6) failed: %s", strerror(errno)); struct sockaddr_in serv_addr4; @@ -92,29 +89,28 @@ static bool bind_to_telnet_port_IPv4(int *socketdescriptor) // Bind to IPv4 port serv_addr4.sin_port = htons(config.port); - if(bind(*socketdescriptor, (struct sockaddr *) &serv_addr4, sizeof(serv_addr4)) < 0) + if(bind(socketdescriptor, (struct sockaddr *) &serv_addr4, sizeof(serv_addr4)) < 0) { logg("Error listening on IPv4 port %i: %s (%i)", config.port, strerror(errno), errno); - return false; + return -1; } // The listen system call allows the process to listen on the socket for connections - if(listen(*socketdescriptor, BACKLOG) == -1) + if(listen(socketdescriptor, BACKLOG) == -1) { logg("Error listening on IPv4 socket: %s (%i)", strerror(errno), errno); - return false; + return -1; } logg("Listening on port %i for incoming IPv4 telnet connections", config.port); - return true; + return socketdescriptor; } -static bool bind_to_telnet_port_IPv6(int *socketdescriptor) +static int bind_to_telnet_port_IPv6(void) { // IPv6 socket - *socketdescriptor = socket(AF_INET6, SOCK_STREAM, 0); - - if(*socketdescriptor < 0) + const int socketdescriptor = socket(AF_INET6, SOCK_STREAM, 0); + if(socketdescriptor < 0) { logg("Error opening IPv6 telnet socket: %s (%i)", strerror(errno), errno); exit(EXIT_FAILURE); @@ -124,7 +120,7 @@ static bool bind_to_telnet_port_IPv6(int *socketdescriptor) // stricted to sending and receiving IPv6 packets only. In this // case, an IPv4 and an IPv6 application can bind to a single port // at the same time. - if(setsockopt(*socketdescriptor, IPPROTO_IPV6, IPV6_V6ONLY, &(int){ 1 }, sizeof(int)) != 0) + if(setsockopt(socketdescriptor, IPPROTO_IPV6, IPV6_V6ONLY, &(int){ 1 }, sizeof(int)) != 0) logg("WARN: setting socket to IPv6-only failed: %s", strerror(errno)); // Set SO_REUSEADDR to allow re-binding to the port that has been used @@ -134,7 +130,7 @@ static bool bind_to_telnet_port_IPv6(int *socketdescriptor) // new instance will fail if there were connections open to the previous // instance when you killed it. Those connections will hold the TCP port in // the TIME_WAIT state for 30-120 seconds, so you fall into case 1 above. - if(setsockopt(*socketdescriptor, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) != 0) + if(setsockopt(socketdescriptor, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) != 0) logg("WARN: allowing re-binding (IPv6) failed: %s", strerror(errno)); struct sockaddr_in6 serv_addr; @@ -156,31 +152,30 @@ static bool bind_to_telnet_port_IPv6(int *socketdescriptor) // Bind to IPv6 socket serv_addr.sin6_port = htons(config.port); - if(bind(*socketdescriptor, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) + if(bind(socketdescriptor, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { logg("Error listening on IPv6 port %i: %s (%i)", config.port, strerror(errno), errno); - return false; + return -1; } // The listen system call allows the process to listen on the socket for connections - if(listen(*socketdescriptor, BACKLOG) == -1) + if(listen(socketdescriptor, BACKLOG) == -1) { logg("Error listening on IPv6 socket: %s (%i)", strerror(errno), errno); - return false; + return -1; } logg("Listening on port %i for incoming IPv6 telnet connections", config.port); - return true; + return socketdescriptor; } -static bool bind_to_unix_socket(int *socketdescriptor) +static int bind_to_unix_socket(void) { - *socketdescriptor = socket(AF_LOCAL, SOCK_STREAM, 0); - - if(*socketdescriptor < 0) + const int socketdescriptor = socket(AF_LOCAL, SOCK_STREAM, 0); + if(socketdescriptor < 0) { logg("WARNING: Error opening Unix socket."); - return false; + return -1; } // Make sure unix socket file handle does not exist, if it exists, remove it @@ -198,21 +193,21 @@ static bool bind_to_unix_socket(int *socketdescriptor) // Bind to Unix socket handle errno = 0; - if(bind(*socketdescriptor, (struct sockaddr *) &address, sizeof (address)) != 0) + if(bind(socketdescriptor, (struct sockaddr *) &address, sizeof (address)) != 0) { logg("WARNING: Cannot bind on Unix socket %s: %s (%i)", FTLfiles.socketfile, strerror(errno), errno); - return false; + return -1; } // The listen system call allows the process to listen on the Unix socket for connections - if(listen(*socketdescriptor, BACKLOG) == -1) + if(listen(socketdescriptor, BACKLOG) == -1) { logg("WARNING: Cannot listen on Unix socket: %s (%i)", strerror(errno), errno); - return false; + return -1; } logg("Listening on Unix socket"); - return true; + return socketdescriptor; } void seom(const int sock) @@ -223,7 +218,7 @@ void seom(const int sock) pack_eom(sock); } -void __attribute__ ((format (gnu_printf, 2, 3))) ssend(const int sock, const char *format, ...) +bool __attribute__ ((format (gnu_printf, 5, 6))) _ssend(const int sock, const char *file, const char *func, const int line, const char *format, ...) { char *buffer; va_list args; @@ -232,68 +227,16 @@ void __attribute__ ((format (gnu_printf, 2, 3))) ssend(const int sock, const cha va_end(args); if(bytes > 0 && buffer != NULL) { - write(sock, buffer, bytes); + FTLwrite(sock, buffer, bytes, short_path(file), func, line); free(buffer); } -} - -static int listener(const int sockfd, const char type) -{ - socklen_t socklen = 0; - struct sockaddr_un un_addr; - struct sockaddr_in in4_addr; - struct sockaddr_in6 in6_addr; - - switch(type) - { - case 0: // Unix socket - memset(&un_addr, 0, sizeof(un_addr)); - socklen = sizeof(un_addr); - return accept(sockfd, (struct sockaddr *) &un_addr, &socklen); - - case 4: // Internet socket (IPv4) - memset(&in4_addr, 0, sizeof(in4_addr)); - socklen = sizeof(un_addr); - return accept(sockfd, (struct sockaddr *) &in4_addr, &socklen); - - case 6: // Internet socket (IPv6) - memset(&in6_addr, 0, sizeof(in6_addr)); - socklen = sizeof(un_addr); - return accept(sockfd, (struct sockaddr *) &in6_addr, &socklen); - - default: // Should not happen - logg("Cannot listen on type %i connection, code error!", type); - exit(EXIT_FAILURE); - } -} - -void close_telnet_socket(void) -{ - // Using global variable here - if(telnetfd4 != 0) - close(telnetfd4); - if(telnetfd6 != 0) - close(telnetfd6); -} - -void close_unix_socket(bool unlink_file) -{ - // Using global variable here - if(sock_avail != 0) - close(socketfd); - - // The process has to take care of unlinking the socket file description - // on exit - if(unlink_file) - { - unlink(FTLfiles.socketfile); - } + return errno == 0; } static void *telnet_connection_handler_thread(void *socket_desc) { //Get the socket descriptor - int sock = *(int*)socket_desc; + const int sock = *(int*)socket_desc; // Set connection type to telnet istelnet[sock] = true; @@ -324,7 +267,7 @@ static void *telnet_connection_handler_thread(void *socket_desc) } if(config.debug & DEBUG_API) - logg("New telnet thread for socket %d", *(int*)socket_desc); + logg("New telnet thread for socket %d", sock); // Receive from client ssize_t n; @@ -338,7 +281,7 @@ static void *telnet_connection_handler_thread(void *socket_desc) if(message == NULL) { if(config.debug & DEBUG_API) - logg("Break in telnet thread for socket %d: Memory error", *(int*)socket_desc); + logg("Break in telnet thread for socket %d: Memory error", sock); break; } @@ -346,31 +289,23 @@ static void *telnet_connection_handler_thread(void *socket_desc) memset(client_message, 0, sizeof client_message); // Process received message - process_request(message, &sock); + const bool eom = process_request(message, sock); free(message); - - if(sock == 0) - { - // Client disconnected by sending EOT or ">quit" - if(config.debug & DEBUG_API) - logg("Break in telnet thread for socket %d: Client disconnected", *(int*)socket_desc); - break; - } + if(eom) break; } else if(n == -1) { if(config.debug & DEBUG_API) - logg("Break in telnet thread for socket %d: No data received", *(int*)socket_desc); + logg("Break in telnet thread for socket %d: No data received", sock); break; } } if(config.debug & DEBUG_API) - logg("Terminating telnet thread for socket %d", *(int*)socket_desc); + logg("Terminating telnet thread for socket %d", sock); - // Free the socket pointer - if(sock != 0) - close(sock); + // Close the socket pointer + close(sock); free(socket_desc); // Release thread from list @@ -383,7 +318,7 @@ static void *telnet_connection_handler_thread(void *socket_desc) static void *socket_connection_handler_thread(void *socket_desc) { //Get the socket descriptor - int sock = *(int*)socket_desc; + const int sock = *(int*)socket_desc; // Set connection type to not telnet istelnet[sock] = false; @@ -420,7 +355,7 @@ static void *socket_connection_handler_thread(void *socket_desc) // Receive from client ssize_t n; - while((n = recv(sock,client_message,SOCKETBUFFERLEN-1, 0))) + while((n = recv(sock, client_message, SOCKETBUFFERLEN-1, 0))) { if (n > 0 && n < SOCKETBUFFERLEN) { @@ -433,14 +368,9 @@ static void *socket_connection_handler_thread(void *socket_desc) memset(client_message, 0, sizeof client_message); // Process received message - process_request(message, &sock); + const bool eom = process_request(message, sock); free(message); - - if(sock == 0) - { - // Socket connection interrupted by sending EOT or ">quit" - break; - } + if(eom) break; } else if(n == -1) { @@ -448,9 +378,8 @@ static void *socket_connection_handler_thread(void *socket_desc) } } - // Free the socket pointer - if(sock != 0) - close(sock); + // Close the socket pointer + close(sock); free(socket_desc); // Release thread from list @@ -474,43 +403,58 @@ void *telnet_listening_thread_IPv4(void *args) thread_names[TELNETv4] = "telnet-IPv4"; prctl(PR_SET_NAME, thread_names[TELNETv4], 0, 0, 0); - // Initialize IPv4 telnet socket - ipv4telnet = bind_to_telnet_port_IPv4(&telnetfd4); - if(!ipv4telnet) - return NULL; - - thread_cancellable[TELNETv4] = true; - - // Listen as long as this thread is not canceled while(!killed) { - // Look for new clients that want to connect - const int csck = listener(telnetfd4, 4); - if(csck == -1) - { - logg("IPv4 telnet error: %s (%i)", strerror(errno), errno); - continue; - } + // Initialize IPv4 telnet socket´ + const int telnetfd4 = bind_to_telnet_port_IPv4(); + if(telnetfd4 < 0) + return NULL; if(config.debug & DEBUG_API) - logg("Accepting new telnet connection at socket %d", csck); + logg("Telnet-IPv4 listener accepting on fd %d", telnetfd4); - // Allocate memory used to transport client socket ID to client listening thread - int *newsock; - newsock = calloc(1,sizeof(int)); - if(newsock == NULL) break; - *newsock = csck; + thread_cancellable[TELNETv4] = true; - pthread_t telnet_connection_thread; - // Create a new thread - if(pthread_create( &telnet_connection_thread, &attr, telnet_connection_handler_thread, (void*) newsock ) != 0) + // Listen as long as this thread is not canceled + int errors = 0; + while(!killed) { - // Log the error code description - logg("WARNING: Unable to open telnet processing thread: %s", strerror(errno)); + // Look for new clients that want to connect + const int csck = accept(telnetfd4, NULL, NULL); + if(csck == -1) + { + logg("IPv4 telnet error: %s (%i, sockfd: %d)", strerror(errno), errno, telnetfd4); + if(errors++ > 20) + break; + continue; + } + + if(config.debug & DEBUG_API) + logg("Accepting new telnet connection at socket %d", csck); + + // Allocate memory used to transport client socket ID to client listening thread + int *newsock; + newsock = calloc(1,sizeof(int)); + if(newsock == NULL) break; + *newsock = csck; + + pthread_t telnet_connection_thread; + // Create a new thread + if(pthread_create( &telnet_connection_thread, &attr, telnet_connection_handler_thread, (void*) newsock ) != 0) + { + // Log the error code description + logg("WARNING: Unable to open telnet processing thread: %s", strerror(errno)); + } } + + if(config.debug & DEBUG_API) + logg("Telnet-IPv4 listener closed (fd %d)", telnetfd4); + + // Close telnet socket + close(telnetfd4); + thread_sleepms(TELNETv4, 1000); } - if(config.debug & DEBUG_API) - logg("Terminating IPv4 telnet thread"); + logg("Terminating IPv4 telnet thread"); return NULL; } @@ -532,38 +476,53 @@ void *telnet_listening_thread_IPv6(void *args) if(!ipv6_available()) return NULL; - ipv6telnet = bind_to_telnet_port_IPv6(&telnetfd6); - if(!ipv6telnet) - return NULL; - - thread_cancellable[TELNETv6] = true; - - // Listen as long as this thread is not canceled while(!killed) { - // Look for new clients that want to connect - const int csck = listener(telnetfd6, 6); - if(csck == -1) - { - logg("IPv6 telnet error: %s (%i)", strerror(errno), errno); - continue; - } + const int telnetfd6 = bind_to_telnet_port_IPv6(); + if(telnetfd6 < 0) + return NULL; + + if(config.debug & DEBUG_API) + logg("Telnet-IPv6 listener accepting on fd %d", telnetfd6); - // Allocate memory used to transport client socket ID to client listening thread - int *newsock; - newsock = calloc(1,sizeof(int)); - if(newsock == NULL) break; - *newsock = csck; + thread_cancellable[TELNETv6] = true; - pthread_t telnet_connection_thread; - // Create a new thread - if(pthread_create( &telnet_connection_thread, &attr, telnet_connection_handler_thread, (void*) newsock ) != 0) + // Listen as long as this thread is not canceled + int errors = 0; + while(!killed) { - // Log the error code description - logg("WARNING: Unable to open telnet processing thread: %s", strerror(errno)); + // Look for new clients that want to connect + const int csck = accept(telnetfd6, NULL, NULL); + if(csck == -1) + { + logg("IPv6 telnet error: %s (%i, sockfd: %d)", strerror(errno), errno, telnetfd6); + if(errors++ > 20) + break; + continue; + } + + // Allocate memory used to transport client socket ID to client listening thread + int *newsock; + newsock = calloc(1,sizeof(int)); + if(newsock == NULL) break; + *newsock = csck; + + pthread_t telnet_connection_thread; + // Create a new thread + if(pthread_create( &telnet_connection_thread, &attr, telnet_connection_handler_thread, (void*) newsock ) != 0) + { + // Log the error code description + logg("WARNING: Unable to open telnet processing thread: %s", strerror(errno)); + } } - } + if(config.debug & DEBUG_API) + logg("Telnet-IPv6 listener closed (fd %d)", telnetfd6); + + // Close telnet socket + close(telnetfd6); + thread_sleepms(TELNETv6, 1000); + } logg("Terminating IPv6 telnet thread"); return NULL; } @@ -582,39 +541,53 @@ void *socket_listening_thread(void *args) thread_names[SOCKET] = "telnet-socket"; prctl(PR_SET_NAME, thread_names[SOCKET], 0, 0, 0); - // Return early to avoid CPU spinning if Unix socket is not available - sock_avail = bind_to_unix_socket(&socketfd); - if(!sock_avail) + while(!killed) { - logg("INFO: Unix socket will not be available"); - return NULL; - } + // Return early to avoid CPU spinning if Unix socket is not available + const int socketfd = bind_to_unix_socket(); + if(socketfd < 0) + { + logg("INFO: Unix socket will not be available"); + return NULL; + } - thread_cancellable[SOCKET] = true; + if(config.debug & DEBUG_API) + logg("Telnet-socket listener accepting on fd %d", socketfd); - // Listen as long as this thread is not canceled - while(!killed) - { - // Look for new clients that want to connect - const int csck = listener(socketfd, 0); - if(csck < 0) - continue; - - // Allocate memory used to transport client socket ID to client listening thread - int *newsock; - newsock = calloc(1,sizeof(int)); - if(newsock == NULL) break; - *newsock = csck; - - pthread_t socket_connection_thread; - // Create a new thread - if(pthread_create( &socket_connection_thread, &attr, socket_connection_handler_thread, (void*) newsock ) != 0) + thread_cancellable[SOCKET] = true; + + // Listen as long as this thread is not canceled + while(!killed) { - // Log the error code description - logg("WARNING: Unable to open socket processing thread: %s", strerror(errno)); + // Look for new clients that want to connect + const int csck = accept(socketfd, NULL, NULL); + if(csck < 0) + continue; + + // Allocate memory used to transport client socket ID to client listening thread + int *newsock; + newsock = calloc(1,sizeof(int)); + if(newsock == NULL) break; + *newsock = csck; + + pthread_t socket_connection_thread; + // Create a new thread + if(pthread_create( &socket_connection_thread, &attr, socket_connection_handler_thread, (void*) newsock ) != 0) + { + // Log the error code description + logg("WARNING: Unable to open socket processing thread: %s", strerror(errno)); + } } - } + if(config.debug & DEBUG_API) + logg("Telnet-socket listener closed (fd %d)", socketfd); + + // Close Unix socket + close(socketfd); + } + // The process has to take care of unlinking the socket file description + // on exit + unlink(FTLfiles.socketfile); logg("Terminating socket thread"); return NULL; } diff --git a/src/api/socket.h b/src/api/socket.h index bfb7150fb..8ee6a173e 100644 --- a/src/api/socket.h +++ b/src/api/socket.h @@ -11,17 +11,16 @@ #define SOCKET_H void saveport(int port); -void close_telnet_socket(void); void close_unix_socket(bool unlink_file); void seom(const int sock); -void ssend(const int sock, const char *format, ...) __attribute__ ((format (gnu_printf, 2, 3))); +#define ssend(sock, format, ...) _ssend(sock, __FILE__, __FUNCTION__, __LINE__, format, ##__VA_ARGS__) +bool _ssend(const int sock, const char *file, const char *func, const int line, const char *format, ...) __attribute__ ((format (gnu_printf, 5, 6))); void *telnet_listening_thread_IPv4(void *args); void *telnet_listening_thread_IPv6(void *args); void *socket_listening_thread(void *args); bool ipv6_available(void); void bind_sockets(void); -extern bool ipv4telnet, ipv6telnet; extern bool istelnet[MAXCONNS]; #endif //SOCKET_H diff --git a/src/daemon.c b/src/daemon.c index a1b26012d..c316f2822 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -14,7 +14,7 @@ #include "log.h" // sleepms() #include "timers.h" -// close_telnet_socket() +// saveport() #include "api/socket.h" // gravityDB_close() #include "database/gravity-db.h" @@ -277,10 +277,6 @@ void cleanup(const int ret) lock_shm(); gravityDB_close(); unlock_shm(); - - // Close sockets and delete Unix socket file handle - close_telnet_socket(); - close_unix_socket(true); } // Empty API port file, port 0 = truncate file diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index 41c122d17..3c989f5fc 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -2851,11 +2851,11 @@ static char *get_ptrname(struct in_addr *addr) } // int cache_inserted, cache_live_freed are defined in dnsmasq/cache.c -void getCacheInformation(const int *sock) +void getCacheInformation(const int sock) { struct cache_info ci; get_dnsmasq_cache_info(&ci); - ssend(*sock,"cache-size: %i\ncache-live-freed: %i\ncache-inserted: %i\nipv4: %i\nipv6: %i\nsrv: %i\ncname: %i\nds: %i\ndnskey: %i\nother: %i\nexpired: %i\nimmortal: %i\n", + ssend(sock, "cache-size: %i\ncache-live-freed: %i\ncache-inserted: %i\nipv4: %i\nipv6: %i\nsrv: %i\ncname: %i\nds: %i\ndnskey: %i\nother: %i\nexpired: %i\nimmortal: %i\n", daemon->cachesize, daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED], daemon->metrics[METRIC_DNS_CACHE_INSERTED], @@ -3133,15 +3133,6 @@ void FTL_TCP_worker_created(const int confd) if(config.debug != 0) logg("Reopening Gravity database for this fork"); gravityDB_forked(); - - // Children inherit file descriptors from their parents - // We don't need them in the forks, so we clean them up - if(config.debug != 0) - logg("Closing Telnet socket for this fork"); - close_telnet_socket(); - if(config.debug != 0) - logg("Closing Unix socket for this fork"); - close_unix_socket(false); } bool FTL_unlink_DHCP_lease(const char *ipaddr) diff --git a/src/dnsmasq_interface.h b/src/dnsmasq_interface.h index 7abca12e7..a35b7381d 100644 --- a/src/dnsmasq_interface.h +++ b/src/dnsmasq_interface.h @@ -15,7 +15,6 @@ #include "edns0.h" -extern int socketfd, telnetfd4, telnetfd6; extern unsigned char* pihole_privacylevel; enum protocol { TCP, UDP, INTERNAL }; diff --git a/src/syscalls/accept.c b/src/syscalls/accept.c index 535f18aa3..5c90d2bf0 100644 --- a/src/syscalls/accept.c +++ b/src/syscalls/accept.c @@ -15,7 +15,7 @@ #undef accept int FTLaccept(int sockfd, struct sockaddr *addr, socklen_t *addrlen, const char *file, const char *func, const int line) { - int ret = 0; + int ret = 0; do { // Reset errno before trying to write @@ -26,11 +26,17 @@ int FTLaccept(int sockfd, struct sockaddr *addr, socklen_t *addrlen, const char // incoming signal while(ret < 0 && errno == EINTR); + // Backup errno value + const int _errno = errno; + // Final error checking (may have failed for some other reason then an // EINTR = interrupted system call) if(ret < 0) logg("WARN: Could not accept() in %s() (%s:%i): %s", - func, file, line, strerror(errno)); + func, file, line, strerror(errno)); + + // Restore errno value + errno = _errno; - return ret; + return ret; } \ No newline at end of file diff --git a/src/syscalls/calloc.c b/src/syscalls/calloc.c index b79ad5fde..546213478 100644 --- a/src/syscalls/calloc.c +++ b/src/syscalls/calloc.c @@ -30,10 +30,17 @@ void* __attribute__((malloc)) __attribute__((alloc_size(1,2))) FTLcalloc(const s // an incoming signal while(ptr == NULL && errno == EINTR); + // Backup errno value + const int _errno = errno; + // Handle other errors than EINTR if(ptr == NULL) logg("FATAL: Memory allocation (%zu x %zu) failed in %s() (%s:%i)", nmemb, size, func, file, line); + // Restore errno value + errno = _errno; + + // Return memory pointer return ptr; } diff --git a/src/syscalls/fopen.c b/src/syscalls/fopen.c index f11c43751..d6416533e 100644 --- a/src/syscalls/fopen.c +++ b/src/syscalls/fopen.c @@ -28,6 +28,9 @@ FILE *FTLfopen(const char *pathname, const char *mode, const char *file, const c // incoming signal while(file_ptr == NULL && errno == EINTR); + // Backup errno value + const int _errno = errno; + // Final error checking (may have failed for some other reason then an // EINTR = interrupted system call) // The already_writing counter prevents a possible infinite loop @@ -38,5 +41,9 @@ FILE *FTLfopen(const char *pathname, const char *mode, const char *file, const c // Decrement warning counter already_writing--; + // Restore errno value + errno = _errno; + + // Return file pointer return file_ptr; } diff --git a/src/syscalls/ftlallocate.c b/src/syscalls/ftlallocate.c index 91597cb71..56bc3944c 100644 --- a/src/syscalls/ftlallocate.c +++ b/src/syscalls/ftlallocate.c @@ -35,5 +35,6 @@ int FTLfallocate(const int fd, const off_t offset, const off_t len, const char * // Set errno ourselves as posix_fallocate doesn't do it errno = ret; + return ret; } diff --git a/src/syscalls/pthread_mutex_lock.c b/src/syscalls/pthread_mutex_lock.c index 2b6c942a0..5f66549ed 100644 --- a/src/syscalls/pthread_mutex_lock.c +++ b/src/syscalls/pthread_mutex_lock.c @@ -17,7 +17,7 @@ #undef pthread_mutex_lock int FTLpthread_mutex_lock(pthread_mutex_t *__mutex, const char *file, const char *func, const int line) { - ssize_t ret = 0; + ssize_t ret = 0; do { // Reset errno before trying to write @@ -28,11 +28,17 @@ int FTLpthread_mutex_lock(pthread_mutex_t *__mutex, const char *file, const char // incoming signal while(ret < 0 && errno == EINTR); + // Backup errno value + const int _errno = errno; + // Final error checking (may have failed for some other reason then an // EINTR = interrupted system call) if(ret < 0) logg("WARN: Could not pthread_mutex_lock() in %s() (%s:%i): %s", - func, file, line, strerror(errno)); + func, file, line, strerror(errno)); + + // Restore errno value + errno = _errno; - return ret; + return ret; } \ No newline at end of file diff --git a/src/syscalls/realloc.c b/src/syscalls/realloc.c index 53df93b1e..9bdb3fa59 100644 --- a/src/syscalls/realloc.c +++ b/src/syscalls/realloc.c @@ -34,10 +34,16 @@ void __attribute__((alloc_size(2))) *FTLrealloc(void *ptr_in, const size_t size, // an incoming signal while(ptr_out == NULL && errno == EINTR); + // Backup errno value + const int _errno = errno; + // Handle other errors than EINTR if(ptr_out == NULL) logg("FATAL: Memory reallocation (%p -> %zu) failed in %s() (%s:%i)", ptr_in, size, func, file, line); + // Restore errno value + errno = _errno; + return ptr_out; } \ No newline at end of file diff --git a/src/syscalls/recv.c b/src/syscalls/recv.c index 999edc4c3..aff6ea56e 100644 --- a/src/syscalls/recv.c +++ b/src/syscalls/recv.c @@ -17,7 +17,7 @@ #undef recv ssize_t FTLrecv(int sockfd, void *buf, size_t len, int flags, const char *file, const char *func, const int line) { - ssize_t ret = 0; + ssize_t ret = 0; do { // Reset errno before trying to write @@ -28,11 +28,17 @@ ssize_t FTLrecv(int sockfd, void *buf, size_t len, int flags, const char *file, // incoming signal while(ret < 0 && errno == EINTR); + // Backup errno value + const int _errno = errno; + // Final error checking (may have failed for some other reason then an // EINTR = interrupted system call) if(ret < 0) logg("WARN: Could not recv() in %s() (%s:%i): %s", - func, file, line, strerror(errno)); + func, file, line, strerror(errno)); + + // Restore errno value + errno = _errno; - return ret; + return ret; } \ No newline at end of file diff --git a/src/syscalls/recvfrom.c b/src/syscalls/recvfrom.c index 322a97275..8db0ac468 100644 --- a/src/syscalls/recvfrom.c +++ b/src/syscalls/recvfrom.c @@ -18,7 +18,7 @@ #undef recvfrom ssize_t FTLrecvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen, const char *file, const char *func, const int line) { - ssize_t ret = 0; + ssize_t ret = 0; do { // Reset errno before trying to write @@ -29,11 +29,17 @@ ssize_t FTLrecvfrom(int sockfd, void *buf, size_t len, int flags, struct sockadd // incoming signal while(ret < 0 && errno == EINTR); + // Backup errno value + const int _errno = errno; + // Final error checking (may have failed for some other reason then an // EINTR = interrupted system call) if(ret < 0) logg("WARN: Could not recvfrom() in %s() (%s:%i): %s", - func, file, line, strerror(errno)); + func, file, line, strerror(errno)); + + // Restore errno value + errno = _errno; - return ret; + return ret; } \ No newline at end of file diff --git a/src/syscalls/select.c b/src/syscalls/select.c index 41c384ca0..4e73b6c35 100644 --- a/src/syscalls/select.c +++ b/src/syscalls/select.c @@ -17,7 +17,7 @@ #undef select int FTLselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout, const char *file, const char *func, const int line) { - int ret = 0; + int ret = 0; do { // Reset errno before trying to write @@ -28,11 +28,17 @@ int FTLselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, st // incoming signal while(ret < 0 && errno == EINTR); + // Backup errno value + const int _errno = errno; + // Final error checking (may have failed for some other reason then an // EINTR = interrupted system call) if(ret < 0) logg("WARN: Could not select() in %s() (%s:%i): %s", - func, file, line, strerror(errno)); + func, file, line, strerror(errno)); + + // Restore errno value + errno = _errno; - return ret; + return ret; } \ No newline at end of file diff --git a/src/syscalls/sendto.c b/src/syscalls/sendto.c index db5e4ff4a..cc7234c94 100644 --- a/src/syscalls/sendto.c +++ b/src/syscalls/sendto.c @@ -18,7 +18,7 @@ #undef sendto ssize_t FTLsendto(int sockfd, void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen, const char *file, const char *func, const int line) { - ssize_t ret = 0; + ssize_t ret = 0; do { // Reset errno before trying to write @@ -29,11 +29,17 @@ ssize_t FTLsendto(int sockfd, void *buf, size_t len, int flags, const struct soc // incoming signal while(ret < 0 && errno == EINTR); + // Backup errno value + const int _errno = errno; + // Final error checking (may have failed for some other reason then an // EINTR = interrupted system call) if(ret < 0) logg("WARN: Could not sendto() in %s() (%s:%i): %s", - func, file, line, strerror(errno)); + func, file, line, strerror(errno)); + + // Restore errno value + errno = _errno; - return ret; + return ret; } \ No newline at end of file diff --git a/src/syscalls/vasprintf.c b/src/syscalls/vasprintf.c index 8300a8c5d..3ac340e60 100644 --- a/src/syscalls/vasprintf.c +++ b/src/syscalls/vasprintf.c @@ -54,6 +54,9 @@ int FTLvasprintf(const char *file, const char *func, const int line, char **buff stdout, _errno, format, func, file, line); } + // Restore errno value + errno = _errno; + // Return number of written bytes return length; } diff --git a/src/syscalls/vfprintf.c b/src/syscalls/vfprintf.c index b945ae4f7..7a5ffae9f 100644 --- a/src/syscalls/vfprintf.c +++ b/src/syscalls/vfprintf.c @@ -134,6 +134,9 @@ int FTLvfprintf(FILE *stream, const char *file, const char *func, const int line if(buffer != NULL) free(buffer); + // Restore errno value + errno = _errno; + // Return early, there isn't anything we can do here return length; } @@ -152,6 +155,9 @@ int FTLvfprintf(FILE *stream, const char *file, const char *func, const int line // to an interruption by an incoming signal while(_buffer < buffer && errno == EINTR); + // Backup errno value + _errno = errno; + // Final error checking (may have failed for some other reason then an // EINTR = interrupted system call) if(_buffer < buffer) @@ -163,6 +169,9 @@ int FTLvfprintf(FILE *stream, const char *file, const char *func, const int line // Free allocated memory free(buffer); + // Restore errno value + errno = _errno; + // Return number of written bytes return length; } diff --git a/src/syscalls/vsnprintf.c b/src/syscalls/vsnprintf.c index 2e1b5f088..4f4badfc0 100644 --- a/src/syscalls/vsnprintf.c +++ b/src/syscalls/vsnprintf.c @@ -47,6 +47,10 @@ int FTLvsnprintf(const char *file, const char *func, const int line, char *__res stdout, _errno, format, func, file, line); } + + // Restore errno value + errno = _errno; + // Return number of written bytes return length; } diff --git a/src/syscalls/vsprintf.c b/src/syscalls/vsprintf.c index b31ba4142..cce0b35fa 100644 --- a/src/syscalls/vsprintf.c +++ b/src/syscalls/vsprintf.c @@ -54,6 +54,9 @@ int FTLvsprintf(const char *file, const char *func, const int line, char *__rest stdout, _errno, format, func, file, line); } + // Restore errno value + errno = _errno; + // Return number of written bytes return length; } diff --git a/src/syscalls/write.c b/src/syscalls/write.c index fc7ee2f18..0aba271f5 100644 --- a/src/syscalls/write.c +++ b/src/syscalls/write.c @@ -21,26 +21,33 @@ ssize_t FTLwrite(int fd, const void *buf, size_t total, const char *file, const return 0; } - ssize_t ret = 0; - size_t written = 0; + ssize_t ret = 0; + size_t written = 0; do { // Reset errno before trying to write errno = 0; ret = write(fd, buf, total); - if(ret > 0) - written += ret; + if(ret > 0) + written += ret; } // Try to write the remaining content into the stream if - // (a) we haven't written all the data, however, there was no other error - // (b) the last write() call failed due to an interruption by an incoming signal + // (a) we haven't written all the data, however, there was no other error + // (b) the last write() call failed due to an interruption by an incoming signal while((written < total && errno == 0) || (ret < 0 && errno == EINTR)); + // Backup errno value + const int _errno = errno; + // Final error checking (may have failed for some other reason then an // EINTR = interrupted system call) if(written < total) logg("WARN: Could not write() everything in %s() [%s:%i]: %s", - func, file, line, strerror(errno)); + func, file, line, strerror(errno)); + + // Restore errno value + errno = _errno; - return written; + // Return number of written bytes + return written; } \ No newline at end of file From 3ecb14640c07a2a39c323292ba626de785d50c72 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 30 Jul 2022 13:53:02 +0200 Subject: [PATCH 2/2] Major rewrite of the socket processing routines. Before, we launched a new thread for each incoming connection (more than 40 threads were never allowed). This could potentially be used to DoS FTL by opening and closing telnet sessions in very quick succession. The new behavior is to, instead, launch five (compile-time setting) threads per type (5 for telnet IPv4, 5 for telnet IPv6, and 5 for Unix socket communication) and let them handle incoming connections in a FIFO manner. If too many requests are sent to FTL at once, they will simply have to wait until they are accepted. Signed-off-by: DL6ER --- src/api/api.c | 91 +++--- src/api/api.h | 28 +- src/api/request.c | 34 +-- src/api/request.h | 2 +- src/api/socket.c | 656 ++++++++++------------------------------ src/api/socket.h | 20 +- src/daemon.c | 20 +- src/daemon.h | 5 +- src/dnsmasq_interface.c | 24 +- src/enums.h | 10 +- 10 files changed, 269 insertions(+), 621 deletions(-) diff --git a/src/api/api.c b/src/api/api.c index 32047a2f0..32a270a0d 100644 --- a/src/api/api.c +++ b/src/api/api.c @@ -15,7 +15,7 @@ #include "../shmem.h" // read_setupVarsconf() #include "../setupVars.h" -// istelnet() +// ssend() #include "socket.h" // get_FTL_db_filesize() #include "../files.h" @@ -76,7 +76,7 @@ static int __attribute__((pure)) cmpdesc(const void *a, const void *b) return 0; } -void getStats(const int sock) +void getStats(const int sock, const bool istelnet) { const int blocked = blocked_queries(); const int total = counters->queries; @@ -87,7 +87,7 @@ void getStats(const int sock) percentage = 1e2f*blocked/total; // Send domains being blocked - if(istelnet[sock]) { + if(istelnet) { ssend(sock, "domains_being_blocked %i\n", counters->gravity); } else @@ -106,7 +106,7 @@ void getStats(const int sock) activeclients++; } - if(istelnet[sock]) { + if(istelnet) { ssend(sock, "dns_queries_today %i\nads_blocked_today %i\nads_percentage_today %f\n", total, blocked, percentage); ssend(sock, "unique_domains %i\nqueries_forwarded %i\nqueries_cached %i\n", @@ -145,16 +145,16 @@ void getStats(const int sock) } // Send status - if(istelnet[sock]) { + if(istelnet) { ssend(sock, "status %s\n", blockingstatus ? "enabled" : "disabled"); } else pack_uint8(sock, blockingstatus); } -void getOverTime(const int sock) +void getOverTime(const int sock, const bool istelnet) { - if(istelnet[sock]) + if(istelnet) { for(int slot = 0; slot < OVERTIME_SLOTS; slot++) { @@ -185,7 +185,7 @@ void getOverTime(const int sock) } } -void getTopDomains(const char *client_message, const int sock) +void getTopDomains(const char *client_message, const int sock, const bool istelnet) { int temparray[counters->domains][2], count=10, num; bool audit = false, asc = false; @@ -196,7 +196,7 @@ void getTopDomains(const char *client_message, const int sock) get_privacy_level(NULL); if(config.privacylevel >= PRIVACY_HIDE_DOMAINS) { // Always send the total number of domains, but pretend it's 0 - if(!istelnet[sock]) + if(!istelnet) pack_int32(sock, 0); return; @@ -269,7 +269,7 @@ void getTopDomains(const char *client_message, const int sock) } } - if(!istelnet[sock]) + if(!istelnet) { // Send the data required to get the percentage each domain has been blocked / queried if(blocked) @@ -306,7 +306,7 @@ void getTopDomains(const char *client_message, const int sock) if(blocked && showblocked && domain->blockedcount > 0) { - if(istelnet[sock]) + if(istelnet) ssend(sock, "%i %i %s\n", n, domain->blockedcount, getstr(domain->domainpos)); else { if(!pack_str32(sock, getstr(domain->domainpos))) @@ -318,7 +318,7 @@ void getTopDomains(const char *client_message, const int sock) } else if(!blocked && showpermitted && (domain->count - domain->blockedcount) > 0) { - if(istelnet[sock]) + if(istelnet) ssend(sock,"%i %i %s\n",n,(domain->count - domain->blockedcount),getstr(domain->domainpos)); else { @@ -339,7 +339,7 @@ void getTopDomains(const char *client_message, const int sock) clearSetupVarsArray(); } -void getTopClients(const char *client_message, const int sock) +void getTopClients(const char *client_message, const int sock, const bool istelnet) { int temparray[counters->clients][2], count=10, num; @@ -347,7 +347,7 @@ void getTopClients(const char *client_message, const int sock) get_privacy_level(NULL); if(config.privacylevel >= PRIVACY_HIDE_DOMAINS_CLIENTS) { // Always send the total number of clients, but pretend it's 0 - if(!istelnet[sock]) + if(!istelnet) pack_int32(sock, 0); return; @@ -408,7 +408,7 @@ void getTopClients(const char *client_message, const int sock) getSetupVarsArray(excludeclients); } - if(!istelnet[sock]) + if(!istelnet) { // Send the total queries so they can make percentages from this data pack_int32(sock, counters->queries); @@ -450,7 +450,7 @@ void getTopClients(const char *client_message, const int sock) // - the client made at least one query within the most recent 24 hours if(includezeroclients || ccount > 0) { - if(istelnet[sock]) + if(istelnet) ssend(sock,"%i %i %s %s\n", n, ccount, client_ip, client_name); else { @@ -471,7 +471,7 @@ void getTopClients(const char *client_message, const int sock) } -void getUpstreamDestinations(const char *client_message, const int sock) +void getUpstreamDestinations(const char *client_message, const int sock, const bool istelnet) { bool sort = true; int temparray[counters->upstreams][2], sumforwarded = 0; @@ -575,7 +575,7 @@ void getUpstreamDestinations(const char *client_message, const int sock) // - only if percentage > 0.0 for all others (i > 0) if(percentage > 0.0f || i < 0) { - if(istelnet[sock]) + if(istelnet) if(upstream_port != 0) ssend(sock, "%i %.2f %s#%u %s#%u\n", i, percentage, ip, upstream_port, name, upstream_port); @@ -592,7 +592,7 @@ void getUpstreamDestinations(const char *client_message, const int sock) } } -void getQueryTypes(const int sock) +void getQueryTypes(const int sock, const bool istelnet) { int total = 0; for(enum query_types type = TYPE_A; type < TYPE_MAX; type++) @@ -611,7 +611,7 @@ void getQueryTypes(const int sock) } } - if(istelnet[sock]) { + if(istelnet) { ssend(sock, "A (IPv4): %.2f\nAAAA (IPv6): %.2f\nANY: %.2f\nSRV: %.2f\n" "SOA: %.2f\nPTR: %.2f\nTXT: %.2f\nNAPTR: %.2f\n" "MX: %.2f\nDS: %.2f\nRRSIG: %.2f\nDNSKEY: %.2f\n" @@ -657,7 +657,7 @@ void getQueryTypes(const int sock) } } -void getAllQueries(const char *client_message, const int sock) +void getAllQueries(const char *client_message, const int sock, const bool istelnet) { // Exit before processing any data if requested via config setting get_privacy_level(NULL); @@ -1053,7 +1053,7 @@ void getAllQueries(const char *client_message, const int sock) delay = 0UL; } - if(istelnet[sock]) + if(istelnet) { ssend(sock,"%lli %s %s %s %i %i %i %lu %s %i %s#%u \"%s\"", (long long)query->timestamp, @@ -1105,7 +1105,7 @@ void getAllQueries(const char *client_message, const int sock) free(clientid_list); } -void getRecentBlocked(const char *client_message, const int sock) +void getRecentBlocked(const char *client_message, const int sock, const bool istelnet) { int num=1; @@ -1132,7 +1132,7 @@ void getRecentBlocked(const char *client_message, const int sock) if(domain == NULL) continue; - if(istelnet[sock]) + if(istelnet) ssend(sock,"%s\n", domain); else if(!pack_str32(sock, domain)) return; @@ -1146,15 +1146,15 @@ void getRecentBlocked(const char *client_message, const int sock) } } -void getClientID(const int sock) +void getClientID(const int sock, const bool istelnet) { - if(istelnet[sock]) + if(istelnet) ssend(sock,"%i\n", sock); else pack_int32(sock, sock); } -void getVersion(const int sock) +void getVersion(const int sock, const bool istelnet) { const char *commit = GIT_HASH; const char *tag = GIT_TAG; @@ -1165,12 +1165,8 @@ void getVersion(const int sock) memcpy(hash, commit, min((size_t)7, strlen(commit))); if(strlen(tag) > 1) { - if(istelnet[sock]) - ssend( - sock, - "version %s\ntag %s\nbranch %s\nhash %s\ndate %s\n", - version, tag, GIT_BRANCH, hash, GIT_DATE - ); + if(istelnet) + ssend(sock, "version %s\ntag %s\nbranch %s\nhash %s\ndate %s\n", version, tag, GIT_BRANCH, hash, GIT_DATE); else { if(!pack_str32(sock, version) || !pack_str32(sock, (char *) tag) || @@ -1181,12 +1177,8 @@ void getVersion(const int sock) } } else { - if(istelnet[sock]) - ssend( - sock, - "version vDev-%s\ntag %s\nbranch %s\nhash %s\ndate %s\n", - hash, tag, GIT_BRANCH, hash, GIT_DATE - ); + if(istelnet) + ssend(sock, "version vDev-%s\ntag %s\nbranch %s\nhash %s\ndate %s\n", hash, tag, GIT_BRANCH, hash, GIT_DATE); else { char *hashVersion = calloc(6 + strlen(hash), sizeof(char)); if(hashVersion == NULL) return; @@ -1204,7 +1196,7 @@ void getVersion(const int sock) } } -void getDBstats(const int sock) +void getDBstats(const int sock, const bool istelnet) { // Get file details unsigned long long int filesize = get_FTL_db_filesize(); @@ -1213,7 +1205,7 @@ void getDBstats(const int sock) double formatted = 0.0; format_memory_size(prefix, filesize, &formatted); - if(istelnet[sock]) + if(istelnet) ssend(sock, "queries in database: %i\ndatabase filesize: %.2f %sB\nSQLite version: %s\n", get_number_of_queries_in_DB(NULL), formatted, prefix, get_sqlite3_version()); else { @@ -1225,7 +1217,7 @@ void getDBstats(const int sock) } } -void getClientsOverTime(const int sock) +void getClientsOverTime(const int sock, const bool istelnet) { // Exit before processing any data if requested via config setting get_privacy_level(NULL); @@ -1263,7 +1255,7 @@ void getClientsOverTime(const int sock) // Main return loop for(int slot = 0; slot < OVERTIME_SLOTS; slot++) { - if(istelnet[sock]) + if(istelnet) ssend(sock, "%lli", (long long)overTime[slot].timestamp); else pack_int32(sock, (int32_t)overTime[slot].timestamp); @@ -1284,13 +1276,13 @@ void getClientsOverTime(const int sock) continue; const int thisclient = client->overTime[slot]; - if(istelnet[sock]) + if(istelnet) ssend(sock, " %i", thisclient); else pack_int32(sock, thisclient); } - if(istelnet[sock]) + if(istelnet) ssend(sock, "\n"); else pack_int32(sock, -1); @@ -1300,7 +1292,7 @@ void getClientsOverTime(const int sock) clearSetupVarsArray(); } -void getClientNames(const int sock) +void getClientNames(const int sock, const bool istelnet) { // Exit before processing any data if requested via config setting get_privacy_level(NULL); @@ -1353,7 +1345,7 @@ void getClientNames(const int sock) const char *client_ip = getstr(client->ippos); const char *client_name = getstr(client->namepos); - if(istelnet[sock]) + if(istelnet) ssend(sock, "%s %s\n", client_name, client_ip); else { pack_str32(sock, client_name); @@ -1365,7 +1357,7 @@ void getClientNames(const int sock) clearSetupVarsArray(); } -void getUnknownQueries(const int sock) +void getUnknownQueries(const int sock, const bool istelnet) { // Exit before processing any data if requested via config setting get_privacy_level(NULL); @@ -1401,7 +1393,7 @@ void getUnknownQueries(const int sock) // Get client IP string const char *clientIP = getstr(client->ippos); - if(istelnet[sock]) + if(istelnet) ssend(sock, "%lli %i %i %s %s %s %i %s\n", (long long)query->timestamp, queryID, query->id, type, getstr(domain->domainpos), clientIP, query->status, query->flags.complete ? "true" : "false"); else { pack_int32(sock, (int32_t)query->timestamp); @@ -1691,6 +1683,7 @@ static bool listInterfaces(struct if_info **head, char default_iface[IF_NAMESIZE rx_sum += new->rx_bytes; } + closedir(dfd); freeifaddrs(ifap); // Create sum entry only if there is more than one interface diff --git a/src/api/api.h b/src/api/api.h index 5867859dc..9e1bbbd8d 100644 --- a/src/api/api.h +++ b/src/api/api.h @@ -11,22 +11,22 @@ #define API_H // Statistic methods -void getStats(const int sock); -void getOverTime(const int sock); -void getTopDomains(const char *client_message, const int sock); -void getTopClients(const char *client_message, const int sock); -void getUpstreamDestinations(const char *client_message, const int sock); -void getQueryTypes(const int sock); -void getAllQueries(const char *client_message, const int sock); -void getRecentBlocked(const char *client_message, const int sock); -void getClientsOverTime(const int sock); -void getClientNames(const int sock); +void getStats(const int sock, const bool istelnet); +void getOverTime(const int sock, const bool istelnet); +void getTopDomains(const char *client_message, const int sock, const bool istelnet); +void getTopClients(const char *client_message, const int sock, const bool istelnet); +void getUpstreamDestinations(const char *client_message, const int sock, const bool istelnet); +void getQueryTypes(const int sock, const bool istelnet); +void getAllQueries(const char *client_message, const int sock, const bool istelnet); +void getRecentBlocked(const char *client_message, const int sock, const bool istelnet); +void getClientsOverTime(const int sock, const bool istelnet); +void getClientNames(const int sock, const bool istelnet); // FTL methods -void getClientID(const int sock); -void getVersion(const int sock); -void getDBstats(const int sock); -void getUnknownQueries(const int sock); +void getClientID(const int sock, const bool istelnet); +void getVersion(const int sock, const bool istelnet); +void getDBstats(const int sock, const bool istelnet); +void getUnknownQueries(const int sock, const bool istelnet); void getMAXLOGAGE(const int sock); void getGateway(const int sock); void getInterfaces(const int sock); diff --git a/src/api/request.c b/src/api/request.c index 88bb5a019..45cdb00a0 100644 --- a/src/api/request.c +++ b/src/api/request.c @@ -26,7 +26,7 @@ bool __attribute__((pure)) command(const char *client_message, const char* cmd) return strstr(client_message, cmd) != NULL; } -bool process_request(const char *client_message, const int sock) +bool process_request(const char *client_message, const int sock, const bool istelnet) { char EOT[2]; EOT[0] = 0x04; @@ -37,104 +37,104 @@ bool process_request(const char *client_message, const int sock) { processed = true; lock_shm(); - getStats(sock); + getStats(sock, istelnet); unlock_shm(); } else if(command(client_message, ">overTime")) { processed = true; lock_shm(); - getOverTime(sock); + getOverTime(sock, istelnet); unlock_shm(); } else if(command(client_message, ">top-domains") || command(client_message, ">top-ads")) { processed = true; lock_shm(); - getTopDomains(client_message, sock); + getTopDomains(client_message, sock, istelnet); unlock_shm(); } else if(command(client_message, ">top-clients")) { processed = true; lock_shm(); - getTopClients(client_message, sock); + getTopClients(client_message, sock, istelnet); unlock_shm(); } else if(command(client_message, ">forward-dest")) { processed = true; lock_shm(); - getUpstreamDestinations(client_message, sock); + getUpstreamDestinations(client_message, sock, istelnet); unlock_shm(); } else if(command(client_message, ">forward-names")) { processed = true; lock_shm(); - getUpstreamDestinations(">forward-dest unsorted", sock); + getUpstreamDestinations(">forward-dest unsorted", sock, istelnet); unlock_shm(); } else if(command(client_message, ">querytypes")) { processed = true; lock_shm(); - getQueryTypes(sock); + getQueryTypes(sock, istelnet); unlock_shm(); } else if(command(client_message, ">getallqueries")) { processed = true; lock_shm(); - getAllQueries(client_message, sock); + getAllQueries(client_message, sock, istelnet); unlock_shm(); } else if(command(client_message, ">recentBlocked")) { processed = true; lock_shm(); - getRecentBlocked(client_message, sock); + getRecentBlocked(client_message, sock, istelnet); unlock_shm(); } else if(command(client_message, ">clientID")) { processed = true; lock_shm(); - getClientID(sock); + getClientID(sock, istelnet); unlock_shm(); } else if(command(client_message, ">version")) { processed = true; // No lock required - getVersion(sock); + getVersion(sock, istelnet); } else if(command(client_message, ">dbstats")) { processed = true; // No lock required. Access to the database // is guaranteed to be atomic - getDBstats(sock); + getDBstats(sock, istelnet); } else if(command(client_message, ">ClientsoverTime")) { processed = true; lock_shm(); - getClientsOverTime(sock); + getClientsOverTime(sock, istelnet); unlock_shm(); } else if(command(client_message, ">client-names")) { processed = true; lock_shm(); - getClientNames(sock); + getClientNames(sock, istelnet); unlock_shm(); } else if(command(client_message, ">unknown")) { processed = true; lock_shm(); - getUnknownQueries(sock); + getUnknownQueries(sock, istelnet); unlock_shm(); } else if(command(client_message, ">cacheinfo")) @@ -199,7 +199,7 @@ bool process_request(const char *client_message, const int sock) ssend(sock, "unknown command: %s\n", client_message); // End of queryable commands: Send EOM - seom(sock); + seom(sock, istelnet); return false; } diff --git a/src/api/request.h b/src/api/request.h index 5e57f5824..ad3ade50f 100644 --- a/src/api/request.h +++ b/src/api/request.h @@ -10,7 +10,7 @@ #ifndef REQUEST_H #define REQUEST_H -bool process_request(const char *client_message, const int sock); +bool process_request(const char *client_message, const int sock, const bool istelnet); bool command(const char *client_message, const char* cmd) __attribute__((pure)); #endif //REQUEST_H diff --git a/src/api/socket.c b/src/api/socket.c index c3d62f864..d03c08f0e 100644 --- a/src/api/socket.c +++ b/src/api/socket.c @@ -14,6 +14,8 @@ #include "socket.h" #include "request.h" #include "../config.h" +// sleepms() +#include "../timers.h" // global variable killed #include "../signals.h" // API thread storage @@ -30,10 +32,6 @@ // reattempt at connection succeeds. #define BACKLOG 5 -// File descriptors -bool dualstack = false; -bool istelnet[MAXCONNS]; - void saveport(int port) { FILE *f; @@ -57,477 +55,180 @@ void saveport(int port) } } -static int bind_to_telnet_port_IPv4(void) +static int bind_to_telnet_socket(const enum telnet_type type, const char *stype) { - // IPv4 socket - const int socketdescriptor = socket(AF_INET, SOCK_STREAM, 0); + const int socketdescriptor = socket(type == TELNET_SOCK ? AF_LOCAL : (type == TELNETv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0); if(socketdescriptor < 0) { - logg("Error opening IPv4 telnet socket: %s (%i)", strerror(errno), errno); + logg("Error opening %s telnet socket: %s (%i)", stype, strerror(errno), errno); exit(EXIT_FAILURE); } - // Set SO_REUSEADDR to allow re-binding to the port that has been used - // previously by FTL. A common pattern is that you change FTL's - // configuration file and need to restart that server to make it reload - // its configuration. Without SO_REUSEADDR, the bind() call in the restarted - // new instance will fail if there were connections open to the previous - // instance when you killed it. Those connections will hold the TCP port in - // the TIME_WAIT state for 30-120 seconds, so you fall into case 1 above. - if(setsockopt(socketdescriptor, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) != 0) - logg("WARN: allowing re-binding (IPv6) failed: %s", strerror(errno)); - - struct sockaddr_in serv_addr4; - // set all values in the buffer to zero - memset(&serv_addr4, 0, sizeof(serv_addr4)); - serv_addr4.sin_family = AF_INET; - - if(config.socket_listenlocal) - serv_addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - else - serv_addr4.sin_addr.s_addr = INADDR_ANY; + const size_t addrlen = MAX(sizeof(struct sockaddr_un), MAX(sizeof(struct sockaddr_in), sizeof(struct sockaddr_in6))); + void *address = calloc(1, addrlen); - // Bind to IPv4 port - serv_addr4.sin_port = htons(config.port); - if(bind(socketdescriptor, (struct sockaddr *) &serv_addr4, sizeof(serv_addr4)) < 0) + if(type == TELNETv4 || type == TELNETv6) { - logg("Error listening on IPv4 port %i: %s (%i)", config.port, strerror(errno), errno); - return -1; - } - - // The listen system call allows the process to listen on the socket for connections - if(listen(socketdescriptor, BACKLOG) == -1) - { - logg("Error listening on IPv4 socket: %s (%i)", strerror(errno), errno); - return -1; - } - - logg("Listening on port %i for incoming IPv4 telnet connections", config.port); - return socketdescriptor; -} + // Set SO_REUSEADDR to allow re-binding to the port that has been used + // previously by FTL. A common pattern is that you change FTL's + // configuration file and need to restart that server to make it reload + // its configuration. Without SO_REUSEADDR, the bind() call in the restarted + // new instance will fail if there were connections open to the previous + // instance when you killed it. Those connections will hold the TCP port in + // the TIME_WAIT state for 30-120 seconds, so you fall into case 1 above. + if(setsockopt(socketdescriptor, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) != 0) + logg("WARN: allowing re-binding (%s) failed: %s", stype, strerror(errno)); + + if(type == TELNETv6) + { + // If this flag is set to true (nonzero), then the socket is re‐ + // stricted to sending and receiving IPv6 packets only. In this + // case, an IPv4 and an IPv6 application can bind to a single port + // at the same time. + if(setsockopt(socketdescriptor, IPPROTO_IPV6, IPV6_V6ONLY, &(int){ 1 }, sizeof(int)) != 0) + logg("WARN: setting socket to IPv6-only failed: %s", strerror(errno)); + + if(config.socket_listenlocal) + ((struct sockaddr_in6*) address)->sin6_addr = in6addr_loopback; + else + ((struct sockaddr_in6*) address)->sin6_addr = in6addr_any; + + // The bind() system call binds a socket to an address, + // in this case the address of the current host and + // port number on which the server will run. + // convert this to network byte order using the function htons() + // which converts a port number in host byte order to a port number + // in network byte order + + // Bind to IPv6 socket + ((struct sockaddr_in6*) address)->sin6_family = AF_INET6; + ((struct sockaddr_in6*) address)->sin6_port = htons(config.port); + } + else // IPv4 + { -static int bind_to_telnet_port_IPv6(void) -{ - // IPv6 socket - const int socketdescriptor = socket(AF_INET6, SOCK_STREAM, 0); - if(socketdescriptor < 0) - { - logg("Error opening IPv6 telnet socket: %s (%i)", strerror(errno), errno); - exit(EXIT_FAILURE); - } + if(config.socket_listenlocal) + ((struct sockaddr_in*) address)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + else + ((struct sockaddr_in*) address)->sin_addr.s_addr = INADDR_ANY; - // If this flag is set to true (nonzero), then the socket is re‐ - // stricted to sending and receiving IPv6 packets only. In this - // case, an IPv4 and an IPv6 application can bind to a single port - // at the same time. - if(setsockopt(socketdescriptor, IPPROTO_IPV6, IPV6_V6ONLY, &(int){ 1 }, sizeof(int)) != 0) - logg("WARN: setting socket to IPv6-only failed: %s", strerror(errno)); - - // Set SO_REUSEADDR to allow re-binding to the port that has been used - // previously by FTL. A common pattern is that you change FTL's - // configuration file and need to restart that server to make it reload - // its configuration. Without SO_REUSEADDR, the bind() call in the restarted - // new instance will fail if there were connections open to the previous - // instance when you killed it. Those connections will hold the TCP port in - // the TIME_WAIT state for 30-120 seconds, so you fall into case 1 above. - if(setsockopt(socketdescriptor, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) != 0) - logg("WARN: allowing re-binding (IPv6) failed: %s", strerror(errno)); - - struct sockaddr_in6 serv_addr; - // set all values in the buffer to zero - memset(&serv_addr, 0, sizeof(serv_addr)); - serv_addr.sin6_family = AF_INET6; - - if(config.socket_listenlocal) - serv_addr.sin6_addr = in6addr_loopback; - else - serv_addr.sin6_addr = in6addr_any; - - // The bind() system call binds a socket to an address, - // in this case the address of the current host and - // port number on which the server will run. - // convert this to network byte order using the function htons() - // which converts a port number in host byte order to a port number - // in network byte order - - // Bind to IPv6 socket - serv_addr.sin6_port = htons(config.port); - if(bind(socketdescriptor, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) - { - logg("Error listening on IPv6 port %i: %s (%i)", config.port, strerror(errno), errno); - return -1; + // Bind to IPv4 port + ((struct sockaddr_in*) address)->sin_family = AF_INET; + ((struct sockaddr_in*) address)->sin_port = htons(config.port); + } } - - // The listen system call allows the process to listen on the socket for connections - if(listen(socketdescriptor, BACKLOG) == -1) + else // socket { - logg("Error listening on IPv6 socket: %s (%i)", strerror(errno), errno); - return -1; - } - - logg("Listening on port %i for incoming IPv6 telnet connections", config.port); - return socketdescriptor; -} + // Make sure unix socket file handle does not exist, if it exists, remove it + unlink(FTLfiles.socketfile); + + ((struct sockaddr_un*) address)->sun_family = AF_LOCAL; + // The sockaddr_un.sum_path may be shorter than the size of the FTLfiles.socketfile + // buffer. Ensure that the string is null-terminated even when the string is too large. + // In case strlen(FTLfiles.socketfile) < sizeof(address.sun_path) [this will virtually + // always be the case], the explicit setting of the last byte to zero is a no-op as + // strncpy() writes additional null bytes to ensure that a total of n bytes are written. + strncpy(((struct sockaddr_un*) address)->sun_path, FTLfiles.socketfile, sizeof(((struct sockaddr_un*) address)->sun_path)); + ((struct sockaddr_un*) address)->sun_path[sizeof(((struct sockaddr_un*) address)->sun_path)-1] = '\0'; -static int bind_to_unix_socket(void) -{ - const int socketdescriptor = socket(AF_LOCAL, SOCK_STREAM, 0); - if(socketdescriptor < 0) - { - logg("WARNING: Error opening Unix socket."); - return -1; } - // Make sure unix socket file handle does not exist, if it exists, remove it - unlink(FTLfiles.socketfile); - - struct sockaddr_un address; - address.sun_family = AF_LOCAL; - // The sockaddr_un.sum_path may be shorter than the size of the FTLfiles.socketfile - // buffer. Ensure that the string is null-terminated even when the string is too large. - // In case strlen(FTLfiles.socketfile) < sizeof(address.sun_path) [this will virtually - // always be the case], the explicit setting of the last byte to zero is a no-op as - // strncpy() writes additional null bytes to ensure that a total of n bytes are written. - strncpy(address.sun_path, FTLfiles.socketfile, sizeof(address.sun_path)); - address.sun_path[sizeof(address.sun_path)-1] = '\0'; - - // Bind to Unix socket handle - errno = 0; - if(bind(socketdescriptor, (struct sockaddr *) &address, sizeof (address)) != 0) + // Bind to socket + if(bind(socketdescriptor, (struct sockaddr *) address, addrlen) < 0) { - logg("WARNING: Cannot bind on Unix socket %s: %s (%i)", FTLfiles.socketfile, strerror(errno), errno); + logg("Error listening on Unix socket %s: %s (%i)", FTLfiles.socketfile, strerror(errno), errno); return -1; } - // The listen system call allows the process to listen on the Unix socket for connections + // The listen system call allows the process to listen on the socket for connections if(listen(socketdescriptor, BACKLOG) == -1) { - logg("WARNING: Cannot listen on Unix socket: %s (%i)", strerror(errno), errno); + logg("Error listening on %s socket: %s (%i)", stype, strerror(errno), errno); return -1; } - logg("Listening on Unix socket"); + logg("Listening on port %i for incoming %s telnet connections", config.port, stype); return socketdescriptor; } -void seom(const int sock) -{ - if(istelnet[sock]) - ssend(sock, "---EOM---\n\n"); - else - pack_eom(sock); -} - -bool __attribute__ ((format (gnu_printf, 5, 6))) _ssend(const int sock, const char *file, const char *func, const int line, const char *format, ...) +static void *telnet_connection_handler_thread(void *args) { - char *buffer; - va_list args; - va_start(args, format); - int bytes = vasprintf(&buffer, format, args); - va_end(args); - if(bytes > 0 && buffer != NULL) - { - FTLwrite(sock, buffer, bytes, short_path(file), func, line); - free(buffer); - } - return errno == 0; -} - -static void *telnet_connection_handler_thread(void *socket_desc) -{ - //Get the socket descriptor - const int sock = *(int*)socket_desc; - // Set connection type to telnet - istelnet[sock] = true; - - // Define buffer for client's message - char client_message[SOCKETBUFFERLEN] = ""; - + struct thread_info *tinfo = args; // Set thread name char threadname[16] = { 0 }; - sprintf(threadname, "telnet-%i", sock); - prctl(PR_SET_NAME, threadname, 0, 0, 0); - - // Store TID of this thread - lock_shm(); - unsigned int tid; - for(tid = 0; tid < MAX_API_THREADS; tid++) - { - if(api_threads[tid] == 0) - { - api_threads[tid] = pthread_self(); - break; - } - } - unlock_shm(); - if(tid == MAX_API_THREADS) - { - logg("Not able to spawn new API thread, limit of " str(MAX_API_THREADS) " threads reached."); - return NULL; - } - - if(config.debug & DEBUG_API) - logg("New telnet thread for socket %d", sock); - - // Receive from client - ssize_t n; - while((n = recv(sock, client_message, SOCKETBUFFERLEN-1, 0))) - { - if (n > 0 && n < SOCKETBUFFERLEN) - { - // Null-terminate client string - client_message[n] = '\0'; - char *message = strdup(client_message); - if(message == NULL) - { - if(config.debug & DEBUG_API) - logg("Break in telnet thread for socket %d: Memory error", sock); - break; - } - - // Clear client message receive buffer - memset(client_message, 0, sizeof client_message); - - // Process received message - const bool eom = process_request(message, sock); - free(message); - if(eom) break; - } - else if(n == -1) - { - if(config.debug & DEBUG_API) - logg("Break in telnet thread for socket %d: No data received", sock); - break; - } - } - - if(config.debug & DEBUG_API) - logg("Terminating telnet thread for socket %d", sock); - - // Close the socket pointer - close(sock); - free(socket_desc); - - // Release thread from list - api_threads[tid] = 0; - - return NULL; -} - - -static void *socket_connection_handler_thread(void *socket_desc) -{ - //Get the socket descriptor - const int sock = *(int*)socket_desc; - // Set connection type to not telnet - istelnet[sock] = false; - - // Define buffer for client's message - char client_message[SOCKETBUFFERLEN] = ""; - - // Set thread name - char threadname[16]; - sprintf(threadname, "socket-%i", sock); + snprintf(threadname, sizeof(threadname), "telnet-%s-%i", tinfo->stype, tinfo->tid); prctl(PR_SET_NAME, threadname, 0, 0, 0); // Ensure this thread can be canceled at any time (not only at // cancellation points) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - // Store TID of this thread - lock_shm(); - unsigned int tid; - for(tid = 0; tid < MAX_API_THREADS; tid++) - { - if(api_threads[tid] == 0) - { - api_threads[tid] = pthread_self(); - api_tids[tid] = gettid(); - break; - } - } - unlock_shm(); - if(tid == MAX_API_THREADS) - { - logg("Not able to spawn new API thread, limit of " str(MAX_API_THREADS) " threads reached."); - return NULL; - } + if(config.debug & DEBUG_API) + logg("Started telnet thread %s", threadname); - // Receive from client - ssize_t n; - while((n = recv(sock, client_message, SOCKETBUFFERLEN-1, 0))) + // Listen as long as this thread is not canceled + int errors = 0; + while(!killed) { - if (n > 0 && n < SOCKETBUFFERLEN) - { - // Null-terminate client string - client_message[n] = '\0'; - char *message = strdup(client_message); - if(message == NULL) break; - - // Clear client message receive buffer - memset(client_message, 0, sizeof client_message); - - // Process received message - const bool eom = process_request(message, sock); - free(message); - if(eom) break; - } - else if(n == -1) + // Look for new clients that want to connect + const int csck = accept(tinfo->fd, NULL, NULL); + if(csck == -1) { - break; + logg("Telnet error in %s: %s (%i, fd: %d)", threadname, strerror(errno), errno, tinfo->fd); + if(errors++ > 20) + break; + sleepms(100); + continue; } - } - - // Close the socket pointer - close(sock); - free(socket_desc); - - // Release thread from list - api_threads[tid] = 0; - api_tids[tid] = 0; - - return NULL; -} - -void *telnet_listening_thread_IPv4(void *args) -{ - // We will use the attributes object later to start all threads in detached mode - pthread_attr_t attr; - // Initialize thread attributes object with default attribute values - pthread_attr_init(&attr); - // When a detached thread terminates, its resources are automatically released back to - // the system without the need for another thread to join with the terminated thread - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - // Set thread name - thread_names[TELNETv4] = "telnet-IPv4"; - prctl(PR_SET_NAME, thread_names[TELNETv4], 0, 0, 0); - - while(!killed) - { - // Initialize IPv4 telnet socket´ - const int telnetfd4 = bind_to_telnet_port_IPv4(); - if(telnetfd4 < 0) - return NULL; - - if(config.debug & DEBUG_API) - logg("Telnet-IPv4 listener accepting on fd %d", telnetfd4); - thread_cancellable[TELNETv4] = true; + // Define buffer for client's message + char client_message[SOCKETBUFFERLEN] ={ 0 }; - // Listen as long as this thread is not canceled - int errors = 0; - while(!killed) + // Receive from client + ssize_t n; + while((n = recv(csck, client_message, SOCKETBUFFERLEN-1, 0))) { - // Look for new clients that want to connect - const int csck = accept(telnetfd4, NULL, NULL); - if(csck == -1) + if (n > 0 && n < SOCKETBUFFERLEN) { - logg("IPv4 telnet error: %s (%i, sockfd: %d)", strerror(errno), errno, telnetfd4); - if(errors++ > 20) + // Null-terminate client string + client_message[n] = '\0'; + char *message = strdup(client_message); + if(message == NULL) + { + if(config.debug & DEBUG_API) + logg("Break in telnet thread for socket %d/%d: Memory error", tinfo->fd, csck); break; - continue; - } - - if(config.debug & DEBUG_API) - logg("Accepting new telnet connection at socket %d", csck); + } - // Allocate memory used to transport client socket ID to client listening thread - int *newsock; - newsock = calloc(1,sizeof(int)); - if(newsock == NULL) break; - *newsock = csck; + // Clear client message receive buffer + memset(client_message, 0, sizeof client_message); - pthread_t telnet_connection_thread; - // Create a new thread - if(pthread_create( &telnet_connection_thread, &attr, telnet_connection_handler_thread, (void*) newsock ) != 0) + // Process received message + const bool eom = process_request(message, csck, tinfo->istelnet); + free(message); + if(eom) break; + } + else if(n == -1) { - // Log the error code description - logg("WARNING: Unable to open telnet processing thread: %s", strerror(errno)); + if(config.debug & DEBUG_API) + logg("Break in telnet thread for socket %d/%d: No data received", tinfo->fd, csck); + break; } } - if(config.debug & DEBUG_API) - logg("Telnet-IPv4 listener closed (fd %d)", telnetfd4); - - // Close telnet socket - close(telnetfd4); - thread_sleepms(TELNETv4, 1000); + // Close client socket + close(csck); } - logg("Terminating IPv4 telnet thread"); - return NULL; -} - -void *telnet_listening_thread_IPv6(void *args) -{ - // We will use the attributes object later to start all threads in detached mode - pthread_attr_t attr; - // Initialize thread attributes object with default attribute values - pthread_attr_init(&attr); - // When a detached thread terminates, its resources are automatically released back to - // the system without the need for another thread to join with the terminated thread - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - // Set thread name - thread_names[TELNETv6] = "telnet-IPv6"; - prctl(PR_SET_NAME, thread_names[TELNETv6], 0, 0, 0); - - // Initialize IPv6 telnet socket but only if IPv6 interfaces are available - if(!ipv6_available()) - return NULL; - - while(!killed) - { - const int telnetfd6 = bind_to_telnet_port_IPv6(); - if(telnetfd6 < 0) - return NULL; - - if(config.debug & DEBUG_API) - logg("Telnet-IPv6 listener accepting on fd %d", telnetfd6); - - thread_cancellable[TELNETv6] = true; - // Listen as long as this thread is not canceled - int errors = 0; - while(!killed) - { - // Look for new clients that want to connect - const int csck = accept(telnetfd6, NULL, NULL); - if(csck == -1) - { - logg("IPv6 telnet error: %s (%i, sockfd: %d)", strerror(errno), errno, telnetfd6); - if(errors++ > 20) - break; - continue; - } - - // Allocate memory used to transport client socket ID to client listening thread - int *newsock; - newsock = calloc(1,sizeof(int)); - if(newsock == NULL) break; - *newsock = csck; - - pthread_t telnet_connection_thread; - // Create a new thread - if(pthread_create( &telnet_connection_thread, &attr, telnet_connection_handler_thread, (void*) newsock ) != 0) - { - // Log the error code description - logg("WARNING: Unable to open telnet processing thread: %s", strerror(errno)); - } - } - - if(config.debug & DEBUG_API) - logg("Telnet-IPv6 listener closed (fd %d)", telnetfd6); + if(config.debug & DEBUG_API) + logg("Terminating telnet thread %s (%d errors)", threadname, errors); - // Close telnet socket - close(telnetfd6); - thread_sleepms(TELNETv6, 1000); - } - logg("Terminating IPv6 telnet thread"); + // Free thread-private memory + free(tinfo); return NULL; } -void *socket_listening_thread(void *args) +void listen_telnet(const enum telnet_type type) { // We will use the attributes object later to start all threads in detached mode pthread_attr_t attr; @@ -537,93 +238,58 @@ void *socket_listening_thread(void *args) // the system without the need for another thread to join with the terminated thread pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - // Set thread name - thread_names[SOCKET] = "telnet-socket"; - prctl(PR_SET_NAME, thread_names[SOCKET], 0, 0, 0); - - while(!killed) + // Initialize telnet socket + const char *stype = type == TELNET_SOCK ? "socket" : (type == TELNETv4 ? "IPv4" : "IPv6"); + const int fd = bind_to_telnet_socket(type, stype); + if(fd < 0) { - // Return early to avoid CPU spinning if Unix socket is not available - const int socketfd = bind_to_unix_socket(); - if(socketfd < 0) - { - logg("INFO: Unix socket will not be available"); - return NULL; - } - - if(config.debug & DEBUG_API) - logg("Telnet-socket listener accepting on fd %d", socketfd); + logg("WARN: Cannot bind to %s telnet socket", stype); + return; + } - thread_cancellable[SOCKET] = true; + if(config.debug & DEBUG_API) + logg("Telnet-%s listener accepting on fd %d", stype, fd); - // Listen as long as this thread is not canceled - while(!killed) + for(unsigned int i = 0; i < MAX_API_THREADS; i++) + { + // Spawn telnet thread + pthread_t telnet_connection_thread; + // Create a private copy of the socket fd for the child thread + struct thread_info *tinfo = calloc(1, sizeof(struct thread_info)); + if(!tinfo) + continue; + + tinfo->fd = fd; + tinfo->tid = i; + tinfo->istelnet = (type == TELNETv4 || type == TELNETv6); + tinfo->stype = stype; + if(pthread_create(&telnet_connection_thread, &attr, telnet_connection_handler_thread, (void*) tinfo) != 0) { - // Look for new clients that want to connect - const int csck = accept(socketfd, NULL, NULL); - if(csck < 0) - continue; - - // Allocate memory used to transport client socket ID to client listening thread - int *newsock; - newsock = calloc(1,sizeof(int)); - if(newsock == NULL) break; - *newsock = csck; - - pthread_t socket_connection_thread; - // Create a new thread - if(pthread_create( &socket_connection_thread, &attr, socket_connection_handler_thread, (void*) newsock ) != 0) - { - // Log the error code description - logg("WARNING: Unable to open socket processing thread: %s", strerror(errno)); - } + // Log the error code description + logg("WARNING: Unable to open telnet processing thread: %s", strerror(errno)); } - - if(config.debug & DEBUG_API) - logg("Telnet-socket listener closed (fd %d)", socketfd); - - // Close Unix socket - close(socketfd); } - // The process has to take care of unlinking the socket file description - // on exit - unlink(FTLfiles.socketfile); - logg("Terminating socket thread"); - return NULL; } -bool ipv6_available(void) +void seom(const int sock, const bool istelnet) { - struct ifaddrs *allInterfaces; - enum { IPv4, IPv6 }; - int iface[2] = { 0 }; - - // Get all interfaces - if (getifaddrs(&allInterfaces) == 0) - { - struct ifaddrs *interface; - // Loop over interfaces - for (interface = allInterfaces; interface != NULL; interface = interface->ifa_next) - { - const unsigned int flags = interface->ifa_flags; - const struct sockaddr *addr = interface->ifa_addr; - - // Check only for up and running IPv4, IPv6 interfaces - if ((flags & (IFF_UP|IFF_RUNNING)) && addr != NULL) - { - iface[addr->sa_family == AF_INET6 ? IPv6 : IPv4]++; - - if(config.debug & DEBUG_NETWORKING) - logg("Interface %s is %s", interface->ifa_name, addr->sa_family == AF_INET6 ? "IPv6" : "IPv4"); - } - } - freeifaddrs(allInterfaces); - } + if(istelnet) + ssend(sock, "---EOM---\n\n"); + else + pack_eom(sock); +} - if(config.debug & DEBUG_NETWORKING) +bool __attribute__ ((format (gnu_printf, 5, 6))) _ssend(const int sock, const char *file, const char *func, const int line, const char *format, ...) +{ + char *buffer; + va_list args; + va_start(args, format); + int bytes = vasprintf(&buffer, format, args); + va_end(args); + if(bytes > 0 && buffer != NULL) { - logg("Found %i IPv4 and %i IPv6 capable interfaces", iface[IPv4], iface[IPv6]); + FTLwrite(sock, buffer, bytes, short_path(file), func, line); + free(buffer); } - - return (iface[IPv6] > 0); + return errno == 0; } diff --git a/src/api/socket.h b/src/api/socket.h index 8ee6a173e..4ee4404b9 100644 --- a/src/api/socket.h +++ b/src/api/socket.h @@ -10,17 +10,21 @@ #ifndef SOCKET_H #define SOCKET_H +// enum telnet_type +#include "../enums.h" + +struct thread_info { + int fd; + int tid; + bool istelnet; + const char *stype; +}; + void saveport(int port); void close_unix_socket(bool unlink_file); -void seom(const int sock); +void seom(const int sock, const bool istelnet); #define ssend(sock, format, ...) _ssend(sock, __FILE__, __FUNCTION__, __LINE__, format, ##__VA_ARGS__) bool _ssend(const int sock, const char *file, const char *func, const int line, const char *format, ...) __attribute__ ((format (gnu_printf, 5, 6))); -void *telnet_listening_thread_IPv4(void *args); -void *telnet_listening_thread_IPv6(void *args); -void *socket_listening_thread(void *args); -bool ipv6_available(void); -void bind_sockets(void); - -extern bool istelnet[MAXCONNS]; +void listen_telnet(const enum telnet_type type); #endif //SOCKET_H diff --git a/src/daemon.c b/src/daemon.c index c316f2822..1c62a7edc 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -29,8 +29,7 @@ #include pthread_t threads[THREADS_MAX] = { 0 }; -pthread_t api_threads[MAX_API_THREADS] = { 0 }; -pid_t api_tids[MAX_API_THREADS] = { 0 }; +pthread_t api_threads[TELNET_MAX][MAX_API_THREADS] = {{ 0 }}; bool resolver_ready = false; void go_daemon(void) @@ -261,16 +260,15 @@ void cleanup(const int ret) terminate_threads(); // Cancel and join possibly still running API worker threads - for(unsigned int tid = 0; tid < MAX_API_THREADS; tid++) + for(enum telnet_type tt = 0; tt < TELNET_MAX; tt++) { - // Skip if this is an unused slot - if(api_threads[tid] == 0) - continue; - - // Otherwise, cancel and join the thread - logg("Joining API worker thread %d", tid); - pthread_cancel(api_threads[tid]); - pthread_join(api_threads[tid], NULL); + for(unsigned int tid = 0; tid < MAX_API_THREADS; tid++) + { + // Otherwise, cancel and join the thread + logg("Joining API worker thread %d/%d", tt,tid); + pthread_cancel(api_threads[tt][tid]); + pthread_join(api_threads[tt][tid], NULL); + } } // Close database connection diff --git a/src/daemon.h b/src/daemon.h index 12b49942e..edcd9227b 100644 --- a/src/daemon.h +++ b/src/daemon.h @@ -12,9 +12,8 @@ #include "enums.h" extern pthread_t threads[THREADS_MAX]; -#define MAX_API_THREADS 40 -extern pthread_t api_threads[MAX_API_THREADS]; -extern pid_t api_tids[MAX_API_THREADS]; +#define MAX_API_THREADS 5 +extern pthread_t api_threads[TELNET_MAX][MAX_API_THREADS]; void go_daemon(void); void savepid(void); diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index 3c989f5fc..dddd4e894 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -2683,26 +2683,10 @@ void FTL_fork_and_bind_sockets(struct passwd *ent_pw) // Initialize thread attributes object with default attribute values pthread_attr_init(&attr); - // Start TELNET IPv4 thread - if(pthread_create( &threads[TELNETv4], &attr, telnet_listening_thread_IPv4, NULL ) != 0) - { - logg("Unable to open IPv4 telnet listening thread. Exiting..."); - exit(EXIT_FAILURE); - } - - // Start TELNET IPv6 thread - if(pthread_create( &threads[TELNETv6], &attr, telnet_listening_thread_IPv6, NULL ) != 0) - { - logg("Unable to open IPv6 telnet listening thread. Exiting..."); - exit(EXIT_FAILURE); - } - - // Start SOCKET thread - if(pthread_create( &threads[SOCKET], &attr, socket_listening_thread, NULL ) != 0) - { - logg("Unable to open Unix socket listening thread. Exiting..."); - exit(EXIT_FAILURE); - } + // Start listening on telnet-like interface + listen_telnet(TELNETv4); + listen_telnet(TELNETv6); + listen_telnet(TELNET_SOCK); // Start database thread if database is used if(pthread_create( &threads[DB], &attr, DB_thread, NULL ) != 0) diff --git a/src/enums.h b/src/enums.h index 361cf3c2e..08982f562 100644 --- a/src/enums.h +++ b/src/enums.h @@ -186,15 +186,19 @@ enum busy_reply { } __attribute__ ((packed)); enum thread_types { - TELNETv4, - TELNETv6, - SOCKET, DB, GC, DNSclient, THREADS_MAX } __attribute__ ((packed)); +enum telnet_type { + TELNETv4, + TELNETv6, + TELNET_SOCK, + TELNET_MAX +} __attribute__ ((packed)); + enum message_type { REGEX_MESSAGE, SUBNET_MESSAGE,