diff options
Diffstat (limited to 'libc/inet')
-rw-r--r-- | libc/inet/Makefile.in | 4 | ||||
-rw-r--r-- | libc/inet/decoded.c | 8 | ||||
-rw-r--r-- | libc/inet/encoded.c | 8 | ||||
-rw-r--r-- | libc/inet/encodeq.c | 2 | ||||
-rw-r--r-- | libc/inet/getaddrinfo.c | 3 | ||||
-rw-r--r-- | libc/inet/getnet.c | 3 | ||||
-rw-r--r-- | libc/inet/hostid.c | 3 | ||||
-rw-r--r-- | libc/inet/ifaddrs.c | 53 | ||||
-rw-r--r-- | libc/inet/resolv.c | 760 | ||||
-rw-r--r-- | libc/inet/sockatmark.c | 38 | ||||
-rw-r--r-- | libc/inet/socketcalls.c | 8 |
11 files changed, 709 insertions, 181 deletions
diff --git a/libc/inet/Makefile.in b/libc/inet/Makefile.in index bed76c30c..5e43f2303 100644 --- a/libc/inet/Makefile.in +++ b/libc/inet/Makefile.in @@ -26,7 +26,7 @@ CSRC-$(V4_OR_V6) += \ inet_lnaof.c inet_netof.c # multi source resolv.c CSRC-$(V4_OR_V6) += \ - encodeh.c decodeh.c encoded.c decoded.c \ + encodeh.c decodeh.c \ encodeq.c encodea.c \ read_etc_hosts_r.c \ dnslookup.c opennameservers.c closenameservers.c \ @@ -50,7 +50,7 @@ socketcalls_CSRC-y += \ sendto.c setsockopt.c shutdown.c socket.c socketpair.c \ recvmmsg.c sendmmsg.c socketcalls_CSRC-$(UCLIBC_LINUX_SPECIFIC) += accept4.c -CSRC-$(UCLIBC_HAS_SOCKET) += $(socketcalls_CSRC-y) opensock.c +CSRC-$(UCLIBC_HAS_SOCKET) += $(socketcalls_CSRC-y) opensock.c sockatmark.c CSRC-$(findstring y,$(UCLIBC_HAS_SOCKET)$(V4_OR_V6)) += ethers.c ether_addr.c diff --git a/libc/inet/decoded.c b/libc/inet/decoded.c deleted file mode 100644 index 378cbfad7..000000000 --- a/libc/inet/decoded.c +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org> - * - * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. - */ - -#define L_decoded -#include RESOLVER diff --git a/libc/inet/encoded.c b/libc/inet/encoded.c deleted file mode 100644 index 27f92becd..000000000 --- a/libc/inet/encoded.c +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org> - * - * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. - */ - -#define L_encoded -#include RESOLVER diff --git a/libc/inet/encodeq.c b/libc/inet/encodeq.c index 5555aa548..dc7945bc6 100644 --- a/libc/inet/encodeq.c +++ b/libc/inet/encodeq.c @@ -5,4 +5,6 @@ */ #define L_encodeq +#define L_ns_name +#define L_res_comp #include RESOLVER diff --git a/libc/inet/getaddrinfo.c b/libc/inet/getaddrinfo.c index a9000ae13..f34a4726c 100644 --- a/libc/inet/getaddrinfo.c +++ b/libc/inet/getaddrinfo.c @@ -959,8 +959,7 @@ getaddrinfo(const char *name, const char *service, last_i = i; if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC)) continue; - /*if (p) - freeaddrinfo works ok on NULL too */ - freeaddrinfo(p); + freeaddrinfo(p); /* freeaddrinfo works ok on NULL too */ return -(i & GAIH_EAI); } if (end) diff --git a/libc/inet/getnet.c b/libc/inet/getnet.c index d90fa701e..8743fb9a5 100644 --- a/libc/inet/getnet.c +++ b/libc/inet/getnet.c @@ -69,6 +69,7 @@ int getnetent_r(struct netent *result_buf, char **tok = NULL; const size_t aliaslen = sizeof(char *) * MAXTOKENS; int ret = ERANGE; + (void)h_errnop; *result = NULL; if (buflen < aliaslen @@ -147,6 +148,7 @@ int getnetbyname_r(const char *name, { register char **cp; int ret, herrnop; + (void)h_errnop; __UCLIBC_MUTEX_LOCK(mylock); setnetent(net_stayopen); @@ -181,6 +183,7 @@ int getnetbyaddr_r(uint32_t net, int type, int *h_errnop) { int ret, herrnop; + (void)h_errnop; __UCLIBC_MUTEX_LOCK(mylock); setnetent(net_stayopen); diff --git a/libc/inet/hostid.c b/libc/inet/hostid.c index 4ddfbd123..5977ada25 100644 --- a/libc/inet/hostid.c +++ b/libc/inet/hostid.c @@ -61,8 +61,7 @@ long int gethostid(void) * Mitch */ if (gethostname(host, HOST_NAME_MAX) >= 0 && *host) { - struct addrinfo hints, *results, *addr; - memset(&hints, 0, sizeof(struct addrinfo)); + struct addrinfo hints = {.ai_family = AF_INET}, *results, *addr; if (!getaddrinfo(host, NULL, &hints, &results)) { for (addr = results; addr; addr = results->ai_next) { /* Just so it doesn't look exactly like the diff --git a/libc/inet/ifaddrs.c b/libc/inet/ifaddrs.c index 0c9310651..72771d35a 100644 --- a/libc/inet/ifaddrs.c +++ b/libc/inet/ifaddrs.c @@ -339,17 +339,19 @@ map_newlink (int idx, struct ifaddrs_storage *ifas, int *map, int max) else if (map[i] == idx) return i; } - /* This should never be reached. If this will be reached, we have - a very big problem. */ - abort (); + + /* This means interfaces changed inbetween the reading of the + RTM_GETLINK and RTM_GETADDR information. We have to repeat + everything. */ + return -1; } /* Create a linked list of `struct ifaddrs' structures, one for each network interface on the host machine. If successful, store the list in *IFAP and return 0. On errors, return -1 and set `errno'. */ -int -getifaddrs (struct ifaddrs **ifap) +static int +getifaddrs_internal (struct ifaddrs **ifap) { struct netlink_handle nh = { 0, 0, 0, NULL, NULL }; struct netlink_res *nlp; @@ -496,6 +498,13 @@ getifaddrs (struct ifaddrs **ifap) kernel. */ ifa_index = map_newlink (ifim->ifi_index - 1, ifas, map_newlink_data, newlink); + if (__builtin_expect (ifa_index == -1, 0)) + { + try_again: + result = -EAGAIN; + free (ifas); + goto exit_free; + } ifas[ifa_index].ifa.ifa_flags = ifim->ifi_flags; while (RTA_OK (rta, rtasize)) @@ -580,9 +589,11 @@ getifaddrs (struct ifaddrs **ifap) that we have holes in the interface part of the list, but we always have already the interface for this address. */ ifa_index = newlink + newaddr_idx; - ifas[ifa_index].ifa.ifa_flags - = ifas[map_newlink (ifam->ifa_index - 1, ifas, - map_newlink_data, newlink)].ifa.ifa_flags; + int idx = map_newlink (ifam->ifa_index - 1, ifas, + map_newlink_data, newlink); + if (__builtin_expect (idx == -1, 0)) + goto try_again; + ifas[ifa_index].ifa.ifa_flags = ifas[idx].ifa.ifa_flags; if (ifa_index > 0) ifas[ifa_index - 1].ifa.ifa_next = &ifas[ifa_index].ifa; ++newaddr_idx; @@ -768,9 +779,13 @@ getifaddrs (struct ifaddrs **ifap) /* If we didn't get the interface name with the address, use the name from the interface entry. */ if (ifas[ifa_index].ifa.ifa_name == NULL) - ifas[ifa_index].ifa.ifa_name - = ifas[map_newlink (ifam->ifa_index - 1, ifas, - map_newlink_data, newlink)].ifa.ifa_name; + { + int idx = map_newlink (ifam->ifa_index - 1, ifas, + map_newlink_data, newlink); + if (__builtin_expect (idx == -1, 0)) + goto try_again; + ifas[ifa_index].ifa.ifa_name = ifas[idx].ifa.ifa_name; + } /* Calculate the netmask. */ if (ifas[ifa_index].ifa.ifa_addr @@ -850,6 +865,22 @@ getifaddrs (struct ifaddrs **ifap) return result; } + + +/* Create a linked list of `struct ifaddrs' structures, one for each + network interface on the host machine. If successful, store the + list in *IFAP and return 0. On errors, return -1 and set `errno'. */ +int +getifaddrs (struct ifaddrs **ifap) +{ + int res; + + do + res = getifaddrs_internal (ifap); + while (res == -EAGAIN); + + return res; +} libc_hidden_def(getifaddrs) void diff --git a/libc/inet/resolv.c b/libc/inet/resolv.c index c05f18980..d7a659a8c 100644 --- a/libc/inet/resolv.c +++ b/libc/inet/resolv.c @@ -11,6 +11,7 @@ /* * Portions Copyright (c) 1985, 1993 * The Regents of the University of California. All rights reserved. + * Portions Copyright © 2021 mirabilos <m@mirbsd.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -247,6 +248,7 @@ Domain name in a message can be represented as either: #include <netdb.h> #include <ctype.h> #include <stdbool.h> +#include <stdint.h> #include <time.h> #include <arpa/nameser.h> #include <sys/utsname.h> @@ -254,6 +256,7 @@ Domain name in a message can be represented as either: #include <sys/stat.h> #include <sys/param.h> #include <bits/uClibc_mutex.h> +#include <fcntl.h> #include "internal/parse_config.h" /* poll() is not supported in kernel <= 2.0, therefore if __NR_poll is @@ -395,14 +398,6 @@ extern int __dns_lookup(const char *name, int type, unsigned char **outpacket, struct resolv_answer *a) attribute_hidden; -extern int __encode_dotted(const char *dotted, - unsigned char *dest, - int maxlen) attribute_hidden; -extern int __decode_dotted(const unsigned char *packet, - int offset, - int packet_len, - char *dest, - int dest_len) attribute_hidden; extern int __encode_header(struct resolv_header *h, unsigned char *dest, int maxlen) attribute_hidden; @@ -416,6 +411,13 @@ extern int __encode_answer(struct resolv_answer *a, int maxlen) attribute_hidden; extern void __open_nameservers(void) attribute_hidden; extern void __close_nameservers(void) attribute_hidden; +extern int __hnbad(const char *dotted) attribute_hidden; + +#define __encode_dotted(dotted,dest,maxlen) \ + dn_comp((dotted), (dest), (maxlen), NULL, NULL) +#define __decode_dotted(packet,offset,packet_len,dest,dest_len) \ + dn_expand((packet), (packet) + (packet_len), (packet) + (offset), \ + (dest), (dest_len)) /* * Theory of operation. @@ -552,116 +554,6 @@ void __decode_header(unsigned char *data, #endif /* L_decodeh */ -#ifdef L_encoded - -/* Encode a dotted string into nameserver transport-level encoding. - This routine is fairly dumb, and doesn't attempt to compress - the data */ -int __encode_dotted(const char *dotted, unsigned char *dest, int maxlen) -{ - unsigned used = 0; - - while (dotted && *dotted) { - char *c = strchr(dotted, '.'); - int l = c ? c - dotted : strlen(dotted); - - /* two consecutive dots are not valid */ - if (l == 0) - return -1; - - if (l >= (maxlen - used - 1)) - return -1; - - dest[used++] = l; - memcpy(dest + used, dotted, l); - used += l; - - if (!c) - break; - dotted = c + 1; - } - - if (maxlen < 1) - return -1; - - dest[used++] = 0; - - return used; -} -#endif /* L_encoded */ - - -#ifdef L_decoded - -/* Decode a dotted string from nameserver transport-level encoding. - This routine understands compressed data. */ -int __decode_dotted(const unsigned char *packet, - int offset, - int packet_len, - char *dest, - int dest_len) -{ - unsigned b; - bool measure = 1; - unsigned total = 0; - unsigned used = 0; - unsigned maxiter = 256; - - if (!packet) - return -1; - - dest[0] = '\0'; - while (--maxiter) { - if (offset >= packet_len) - return -1; - b = packet[offset++]; - if (b == 0) - break; - - if (measure) - total++; - - if ((b & 0xc0) == 0xc0) { - if (offset >= packet_len) - return -1; - if (measure) - total++; - /* compressed item, redirect */ - offset = ((b & 0x3f) << 8) | packet[offset]; - measure = 0; - continue; - } - - if (used + b + 1 >= dest_len) - return -1; - if (offset + b >= packet_len) - return -1; - memcpy(dest + used, packet + offset, b); - offset += b; - used += b; - - if (measure) - total += b; - - if (packet[offset] != 0) - dest[used++] = '.'; - else - dest[used++] = '\0'; - } - if (!maxiter) - return -1; - - /* The null byte must be counted too */ - if (measure) - total++; - - DPRINTF("Total decode len = %d\n", total); - - return total; -} -#endif /* L_decoded */ - - #ifdef L_encodeq int __encode_question(const struct resolv_question *q, @@ -1155,6 +1047,263 @@ static int __decode_answer(const unsigned char *message, /* packet */ return i + RRFIXEDSZ + a->rdlength; } + +#if defined __UCLIBC_DNSRAND_MODE_URANDOM__ || defined __UCLIBC_DNSRAND_MODE_PRNGPLUS__ + +/* + * Get a random int from urandom. + * Return 0 on success and -1 on failure. + * + * This will dip into the entropy pool maintaind by the system. + */ +int _dnsrand_getrandom_urandom(int *rand_value) { + static int urand_fd = -1; + static int errCnt = 0; + if (urand_fd == -1) { + urand_fd = open("/dev/urandom", O_RDONLY); + if (urand_fd == -1) { + if ((errCnt % 16) == 0) { + DPRINTF("uCLibC:WARN:DnsRandGetRand: urandom is unavailable...\n"); + } + errCnt += 1; + return -1; + } + } + if (read(urand_fd, rand_value, sizeof(int)) == sizeof(int)) { /* small reads like few bytes here should be safe in general. */ + DPRINTF("uCLibC:DBUG:DnsRandGetRand: URandom:0x%lx\n", *rand_value); + return 0; + } + return -1; +} + +#endif + +#if defined __UCLIBC_DNSRAND_MODE_CLOCK__ || defined __UCLIBC_DNSRAND_MODE_PRNGPLUS__ + +/* + * Try get a sort of random int by looking at current time in system realtime clock. + * Return 0 on success and -1 on failure. + * + * This requries the realtime related uclibc feature to be enabled and also + * the system should have a clock source with nanosec resolution to be mapped + * to CLOCK_REALTIME, for this to generate values that appear random plausibly. + */ +int _dnsrand_getrandom_clock(int *rand_value) { +#if defined __USE_POSIX199309 && defined __UCLIBC_HAS_REALTIME__ + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts) == 0) { + *rand_value = (ts.tv_sec + ts.tv_nsec) % INT_MAX; + DPRINTF("uCLibC:DBUG:DnsRandGetRand: Clock:0x%lx\n", *rand_value); + return 0; + } +#endif + return -1; +} + +#endif + +#ifdef __UCLIBC_DNSRAND_MODE_PRNGPLUS__ + +/* + * Try get a random int by first checking at urandom and then at realtime clock. + * Return 0 on success and -1 on failure. + * + * Chances are most embedded targets using linux/bsd/... could have urandom and + * also it can potentially give better random values, so try urandom first. + * However if there is failure wrt urandom, then try realtime clock based helper. + */ +int _dnsrand_getrandom_urcl(int *rand_value) { + if (_dnsrand_getrandom_urandom(rand_value) == 0) { + return 0; + } + if (_dnsrand_getrandom_clock(rand_value) == 0) { + return 0; + } + DPRINTF("uCLibC:DBUG:DnsRandGetRand: URCL:Nothing:0x%lx\n", *rand_value); + return -1; +} + +#define DNSRAND_PRNGSTATE_INT32LEN 32 +#undef DNSRAND_PRNGRUN_SHORT +#ifdef DNSRAND_PRNGRUN_SHORT +#define DNSRAND_RESEED_OP1 (DNSRAND_PRNGSTATE_INT32LEN/2) +#define DNSRAND_RESEED_OP2 (DNSRAND_PRNGSTATE_INT32LEN/4) +#else +#define DNSRAND_RESEED_OP1 (DNSRAND_PRNGSTATE_INT32LEN*6) +#define DNSRAND_RESEED_OP2 DNSRAND_PRNGSTATE_INT32LEN +#endif + +#define DNSRAND_TIMEFORCED_RESEED_CHECKMOD (DNSRAND_PRNGSTATE_INT32LEN/8) +#define DNSRAND_TIMEFORCED_RESEED_SECS 120 + +time_t clock_getcursec(void) { + static time_t dummyTime = 0; +#if defined __USE_POSIX199309 && defined __UCLIBC_HAS_REALTIME__ + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts) == 0) { + return ts.tv_sec; + } +#endif + dummyTime += DNSRAND_TIMEFORCED_RESEED_SECS; + return dummyTime; +} + +/* + * This logic uses uclibc's random PRNG to generate random int. This keeps the + * logic fast by not depending on a more involved CPRNG kind of logic nor on a + * kernel to user space handshake at the core. + * + * However to ensure that pseudo random sequences based on a given seeding of the + * PRNG logic, is not generated for too long so as to allow a advarsary to try guess + * the internal states of the prng logic and inturn the next number, srandom is + * used periodically to reseed PRNG logic, when and where possible. + * + * To help with this periodic reseeding, by default the logic will first try to + * see if it can get some relatively random number using /dev/urandom. If not it + * will try use the current time to generate plausibly random value as substitute. + * If neither of these sources are available, then the prng itself is used to seed + * a new state, so that the pseudo random sequence can continue, which is better + * than the fallback simple counter. + * + * Also to add bit more of variance wrt this periodic reseeding, the period interval + * at which this reseeding occurs keeps changing within a predefined window. The + * window is controlled based on how often this logic is called (which currently + * will depend on how often requests for dns query (and inturn dnsrand_next) occurs, + * as well as a self driven periodically changing request count boundry. + * + * The internally generated random values are not directly exposed, instead result + * of adjacent values large mult with mod is used to greatly reduce the possibility + * of trying to infer the internal values from externally exposed random values. + * This should also make longer run of prng ok to an extent. + * + * NOTE: The Random PRNG used here maintains its own internal state data, so that + * it doesnt impact any other users of random prng calls in the system/program + * compiled against uclibc. + * + * NOTE: If your target doesnt support int64_t, then the code uses XOR instead of + * mult with mod based transform on the internal random sequence, to generate the + * random number that is returned. However as XOR is not a one way transform, this + * is supported only in DNSRAND_PRNGRUN_SHORT mode by default, which needs to be + * explicitly enabled by the platform developer, by defining the same. + * + */ +int _dnsrand_getrandom_prng(int *rand_value) { + static time_t reSeededSec = 0; + time_t curSec = 0; + bool bTimeForcedReSeed = 0; + static int cnt = -1; + static int nextReSeedWindow = DNSRAND_RESEED_OP1; + static int32_t prngState[DNSRAND_PRNGSTATE_INT32LEN]; /* prng logic internally assumes int32_t wrt state array, so to help align if required */ + static struct random_data prngData; + int32_t val, val2; + int calc; + int prngSeed = 0x19481869; + + if (cnt == -1) { + _dnsrand_getrandom_urcl(&prngSeed); + memset(&prngData, 0, sizeof(prngData)); + initstate_r(prngSeed, (char*)&prngState, DNSRAND_PRNGSTATE_INT32LEN*4, &prngData); + } + cnt += 1; + if ((cnt % DNSRAND_TIMEFORCED_RESEED_CHECKMOD) == 0) { + curSec = clock_getcursec(); + if ((curSec - reSeededSec) >= DNSRAND_TIMEFORCED_RESEED_SECS) { + bTimeForcedReSeed = 1; + } + } + if (((cnt % nextReSeedWindow) == 0) || bTimeForcedReSeed) { + if (curSec == 0) curSec = clock_getcursec(); + reSeededSec = curSec; + if (_dnsrand_getrandom_urcl(&prngSeed) != 0) { + random_r(&prngData, &prngSeed); + } + srandom_r(prngSeed, &prngData); + random_r(&prngData, &val); + nextReSeedWindow = DNSRAND_RESEED_OP1 + (val % DNSRAND_RESEED_OP2); + DPRINTF("uCLibC:DBUG:DnsRandNext: PRNGWindow:%d\n", nextReSeedWindow); + cnt = 0; + } + random_r(&prngData, &val); + random_r(&prngData, &val2); +#ifdef INT64_MAX + calc = ((int64_t)val * (int64_t)val2) % INT_MAX; +#else +# ifdef DNSRAND_PRNGRUN_SHORT + calc = val ^ val2; +# warning "[No int64] using xor based random number transform logic in short prng run mode, bcas int64_t not supported on this target" +# else +# error "[No int64] using xor based random number transform logic only supported with short prng runs, you may want to define DNSRAND_PRNGRUN_SHORT" +# endif +#endif + *rand_value = calc; + DPRINTF("uCLibC:DBUG:DnsRandGetRand: PRNGPlus: %d, 0x%lx 0x%lx 0x%lx\n", cnt, val, val2, *rand_value); + return 0; +} + +#endif + +/** + * If DNS query's id etal is generated using a simple counter, then it can be + * subjected to dns poisoning relatively easily, so adding some randomness can + * increase the difficulty wrt dns poisoning and is thus desirable. + * + * However given that embedded targets may or may not have different sources available + * with them to try generate random values, this logic tries to provides flexibility + * to the platform developer to decide, how they may want to handle this. + * + * If a given target doesnt support urandom nor realtime clock OR for some reason + * if the platform developer doesnt want to use random dns query id etal, then + * they can define __UCLIBC_DNSRAND_MODE_SIMPLECOUNTER__ so that a simple incrementing + * counter is used. + * + * However if the target has support for urandom or realtime clock, then the prngplus + * based random generation tries to give a good balance between randomness and performance. + * This is the default and is enabled when no other mode is defined. It is also indirectly + * enabled by defining __UCLIBC_DNSRAND_MODE_PRNGPLUS__ instead of the other modes. + * + * If urandom is available on the target and one wants to keep things simple and use + * it directly, then one can define __UCLIBC_DNSRAND_MODE_URANDOM__. Do note that this + * will be relatively slower compared to other options. But it can normally generate + * good random values/ids by dipping into the entropy pool available in the system. + * + * If system realtime clock is available on target and enabled, then if one wants to + * keep things simple and use it directly, then define __UCLIBC_DNSRAND_MODE_CLOCK__. + * Do note that this requires nanosecond resolution / granularity wrt the realtime + * clock source to generate plausibly random values/ids. As processor &/ io performance + * improves, the effectiveness of this strategy can be impacted in some cases. + * + * If either the URandom or Clock based get random fails, then the logic is setup to + * try fallback to the simple counter mode, with the help of the def_value, which is + * setup to be the next increment wrt the previously generated / used value, by the + * caller of dnsrand_next. + * + */ +int dnsrand_next(int def_value) { + int val = def_value; +#if defined __UCLIBC_DNSRAND_MODE_SIMPLECOUNTER__ + return val; +#elif defined __UCLIBC_DNSRAND_MODE_URANDOM__ + if (_dnsrand_getrandom_urandom(&val) == 0) { + return val; + } + return def_value; +#elif defined __UCLIBC_DNSRAND_MODE_CLOCK__ + if (_dnsrand_getrandom_clock(&val) == 0) { + return val; + } + return def_value; +#else + if (_dnsrand_getrandom_prng(&val) == 0) { + return val; + } + return def_value; +#endif +} + +int dnsrand_setup(int def_value) { + return def_value; +} + /* On entry: * a.buf(len) = auxiliary buffer for IP addresses after first one * a.add_count = how many additional addresses are there already @@ -1204,6 +1353,7 @@ int __dns_lookup(const char *name, bool ends_with_dot; bool contains_dot; sockaddr46_t sa; + int num_answers; fd = -1; lookup = NULL; @@ -1258,7 +1408,7 @@ int __dns_lookup(const char *name, } /* first time? pick starting server etc */ if (local_ns_num < 0) { - local_id = last_id; + local_id = dnsrand_setup(last_id); /*TODO: implement /etc/resolv.conf's "options rotate" (a.k.a. RES_ROTATE bit in _res.options) local_ns_num = 0; @@ -1268,7 +1418,7 @@ int __dns_lookup(const char *name, } if (local_ns_num >= __nameservers) local_ns_num = 0; - local_id++; + local_id = dnsrand_next(++local_id); local_id &= 0xffff; /* write new values back while still under lock */ last_id = local_id; @@ -1446,6 +1596,7 @@ int __dns_lookup(const char *name, goto fail1; } pos = HFIXEDSZ; + /*XXX TODO: check that question matches query (and qdcount==1?) */ for (j = 0; j < h.qdcount; j++) { DPRINTF("Skipping question %d at %d\n", j, pos); i = __length_question(packet + pos, packet_len - pos); @@ -1460,6 +1611,7 @@ int __dns_lookup(const char *name, DPRINTF("Decoding answer at pos %d\n", pos); first_answer = 1; + num_answers = 0; a->dotted = NULL; for (j = 0; j < h.ancount; j++) { i = __decode_answer(packet, pos, packet_len, &ma); @@ -1467,12 +1619,15 @@ int __dns_lookup(const char *name, DPRINTF("failed decode %d\n", i); /* If the message was truncated but we have * decoded some answers, pretend it's OK */ - if (j && h.tc) + if (num_answers && h.tc) break; goto try_next_server; } pos += i; + if (__hnbad(ma.dotted)) + break; + ++num_answers; if (first_answer) { ma.buf = a->buf; ma.buflen = a->buflen; @@ -1502,6 +1657,10 @@ int __dns_lookup(const char *name, ++a->add_count; } } + if (!num_answers) { + h_errno = NO_RECOVERY; + goto fail1; + } /* Success! */ DPRINTF("Answer name = |%s|\n", a->dotted); @@ -1619,7 +1778,7 @@ int __read_etc_hosts_r( found: result_buf->h_name = *(result_buf->h_aliases++); result_buf->h_addr_list = (char**)(buf + HALISTOFF); - *(result_buf->h_addr_list + 1) = '\0'; + *(result_buf->h_addr_list + 1) = 0; h_addr0 = (struct in_addr*)(buf + INADDROFF); result_buf->h_addr = (char*)h_addr0; if (0) /* nothing */; @@ -2468,7 +2627,7 @@ int gethostbyaddr_r(const void *addr, socklen_t addrlen, /* Decode CNAME into buf, feed it to __dns_lookup() again */ i = __decode_dotted(packet, a.rdoffset, packet_len, buf, buflen); free(packet); - if (i < 0) { + if (i < 0 || __hnbad(buf)) { *h_errnop = NO_RECOVERY; return -1; } @@ -2477,6 +2636,10 @@ int gethostbyaddr_r(const void *addr, socklen_t addrlen, if (a.atype == T_PTR) { /* ADDRESS */ i = __decode_dotted(packet, a.rdoffset, packet_len, buf, buflen); free(packet); + if (__hnbad(buf)) { + *h_errnop = NO_RECOVERY; + return -1; + } result_buf->h_name = buf; result_buf->h_addrtype = type; result_buf->h_length = addrlen; @@ -2646,7 +2809,7 @@ libc_hidden_def(gethostbyaddr) * 'exp_dn' is a pointer to a buffer of size 'length' for the result. * Return size of compressed name or -1 if there was an error. */ -int dn_expand(const u_char *msg, const u_char *eom, const u_char *src, +int weak_function dn_expand(const u_char *msg, const u_char *eom, const u_char *src, char *dst, int dstsiz) { int n = ns_name_uncompress(msg, eom, src, dst, (size_t)dstsiz); @@ -2655,14 +2818,14 @@ int dn_expand(const u_char *msg, const u_char *eom, const u_char *src, dst[0] = '\0'; return n; } -libc_hidden_def(dn_expand) +libc_hidden_weak(dn_expand) /* * Pack domain name 'exp_dn' in presentation form into 'comp_dn'. * Return the size of the compressed name or -1. * 'length' is the size of the array pointed to by 'comp_dn'. */ -int +int weak_function dn_comp(const char *src, u_char *dst, int dstsiz, u_char **dnptrs, u_char **lastdnptr) { @@ -2670,7 +2833,7 @@ dn_comp(const char *src, u_char *dst, int dstsiz, (const u_char **) dnptrs, (const u_char **) lastdnptr); } -libc_hidden_def(dn_comp) +libc_hidden_weak(dn_comp) #endif /* L_res_comp */ @@ -2710,7 +2873,7 @@ static int special(int ch) * note: * Root domain returns as "." not "". */ -int ns_name_uncompress(const u_char *msg, const u_char *eom, +int weak_function ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src, char *dst, size_t dstsiz) { u_char tmp[NS_MAXCDNAME]; @@ -2723,7 +2886,7 @@ int ns_name_uncompress(const u_char *msg, const u_char *eom, return -1; return n; } -libc_hidden_def(ns_name_uncompress) +libc_hidden_weak(ns_name_uncompress) /* * ns_name_ntop(src, dst, dstsiz) @@ -2734,7 +2897,7 @@ libc_hidden_def(ns_name_uncompress) * The root is returned as "." * All other domains are returned in non absolute form */ -int ns_name_ntop(const u_char *src, char *dst, size_t dstsiz) +int weak_function ns_name_ntop(const u_char *src, char *dst, size_t dstsiz) { const u_char *cp; char *dn, *eom; @@ -2804,7 +2967,7 @@ int ns_name_ntop(const u_char *src, char *dst, size_t dstsiz) *dn++ = '\0'; return (dn - dst); } -libc_hidden_def(ns_name_ntop) +libc_hidden_weak(ns_name_ntop) static int encode_bitstring(const char **bp, const char *end, unsigned char **labelp, @@ -2918,7 +3081,7 @@ static int encode_bitstring(const char **bp, const char *end, return 0; } -int ns_name_pton(const char *src, u_char *dst, size_t dstsiz) +int weak_function ns_name_pton(const char *src, u_char *dst, size_t dstsiz) { static const char digits[] = "0123456789"; u_char *label, *bp, *eom; @@ -3039,7 +3202,52 @@ int ns_name_pton(const char *src, u_char *dst, size_t dstsiz) errno = EMSGSIZE; return -1; } -libc_hidden_def(ns_name_pton) +libc_hidden_weak(ns_name_pton) + +/* + * __hnbad(dotted) + * Check whether a name is valid enough for DNS. The rules, as + * laid down by glibc, are: + * - printable input string + * - converts to label notation + * - each label only contains [0-9a-zA-Z_-], up to 63 octets + * - first label doesn’t begin with ‘-’ + * This both is weaker than Unix hostnames (e.g. it allows + * underscores and leading/trailing hyphen-minus) and stronger + * than general (e.g. a leading “*.” is valid sometimes), take care. + * return: + * 0 if the name is ok + */ +int weak_function __hnbad(const char *dotted) +{ + unsigned char c, n, *cp; + unsigned char buf[NS_MAXCDNAME]; + + cp = (unsigned char *)dotted; + while ((c = *cp++)) + if (c < 0x21 || c > 0x7E) + return (1); + if (ns_name_pton(dotted, buf, sizeof(buf)) < 0) + return (2); + if (buf[0] > 0 && buf[1] == '-') + return (3); + cp = buf; + while ((n = *cp++)) { + if (n > 63) + return (4); + while (n--) { + c = *cp++; + if (c < '-' || + (c > '-' && c < '0') || + (c > '9' && c < 'A') || + (c > 'Z' && c < '_') || + (c > '_' && c < 'a') || + c > 'z') + return (5); + } + } + return (0); +} /* * ns_name_unpack(msg, eom, src, dst, dstsiz) @@ -3047,7 +3255,7 @@ libc_hidden_def(ns_name_pton) * return: * -1 if it fails, or consumed octets if it succeeds. */ -int ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src, +int weak_function ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src, u_char *dst, size_t dstsiz) { const u_char *srcp, *dstlim; @@ -3114,7 +3322,7 @@ int ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src, len = srcp - src; return len; } -libc_hidden_def(ns_name_unpack) +libc_hidden_weak(ns_name_unpack) static int labellen(const unsigned char *lp) { @@ -3208,7 +3416,7 @@ next: return -1; } -int ns_name_pack(const unsigned char *src, +int weak_function ns_name_pack(const unsigned char *src, unsigned char *dst, int dstsiz, const unsigned char **dnptrs, const unsigned char **lastdnptr) @@ -3311,15 +3519,15 @@ cleanup: if (msg != NULL) *lpp = NULL; - errno = EMSGSIZE; - return -1; + errno = EMSGSIZE; + return -1; } return dstp - dst; } -libc_hidden_def(ns_name_pack) +libc_hidden_weak(ns_name_pack) -int ns_name_compress(const char *src, +int weak_function ns_name_compress(const char *src, unsigned char *dst, size_t dstsiz, const unsigned char **dnptrs, const unsigned char **lastdnptr) @@ -3331,9 +3539,9 @@ int ns_name_compress(const char *src, return ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr); } -libc_hidden_def(ns_name_compress) +libc_hidden_weak(ns_name_compress) -int ns_name_skip(const unsigned char **ptrptr, +int weak_function ns_name_skip(const unsigned char **ptrptr, const unsigned char *eom) { const unsigned char *cp; @@ -3375,9 +3583,9 @@ int ns_name_skip(const unsigned char **ptrptr, return 0; } -libc_hidden_def(ns_name_skip) +libc_hidden_weak(ns_name_skip) -int dn_skipname(const unsigned char *ptr, const unsigned char *eom) +int weak_function dn_skipname(const unsigned char *ptr, const unsigned char *eom) { const unsigned char *saveptr = ptr; @@ -3386,7 +3594,7 @@ int dn_skipname(const unsigned char *ptr, const unsigned char *eom) return ptr - saveptr; } -libc_hidden_def(dn_skipname) +libc_hidden_weak(dn_skipname) #endif /* L_ns_name */ @@ -3395,7 +3603,7 @@ libc_hidden_def(dn_skipname) /* Will be called under __resolv_lock. */ static void res_sync_func(void) { - struct __res_state *rp = &(_res); + struct __res_state *rp = __res_state(); int n; /* If we didn't get malloc failure earlier... */ @@ -3632,6 +3840,263 @@ void res_close(void) } #endif +#ifdef __UCLIBC_HAS_BSD_B64_NTOP_B64_PTON__ +#define Assert(Cond) if (!(Cond)) abort() + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC 1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + +int +b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) { + size_t datalength = 0; + u_char input[3]; + u_char output[4]; + size_t i; + + while (2 < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + Assert(output[0] < 64); + Assert(output[1] < 64); + Assert(output[2] < 64); + Assert(output[3] < 64); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + target[datalength++] = Base64[output[2]]; + target[datalength++] = Base64[output[3]]; + } + + /* Now we worry about padding. */ + if (0 != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + Assert(output[0] < 64); + Assert(output[1] < 64); + Assert(output[2] < 64); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + if (srclength == 1) + target[datalength++] = Pad64; + else + target[datalength++] = Base64[output[2]]; + target[datalength++] = Pad64; + } + if (datalength >= targsize) + return (-1); + target[datalength] = '\0'; /* Returned value doesn't count \0. */ + return (datalength); +} +/* libc_hidden_def (b64_ntop) */ + +/* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + +int +b64_pton (char const *src, u_char *target, size_t targsize) +{ + int tarindex, state, ch; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (isspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if ((size_t)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = ((pos - Base64) & 0x0f) + << 4 ; + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if ((size_t)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = ((pos - Base64) & 0x03) + << 6; + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + default: + abort(); + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (!isspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (!isspace(ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} +#endif + /* This needs to be after the use of _res in res_init, above. */ #undef _res @@ -3690,7 +4155,8 @@ res_ninit(res_state statp) #endif /* L_res_init */ #ifdef L_res_state -# if defined __UCLIBC_HAS_TLS__ +# if !defined __UCLIBC_HAS_TLS__ +extern struct __res_state *__resp; struct __res_state * __res_state (void) { diff --git a/libc/inet/sockatmark.c b/libc/inet/sockatmark.c new file mode 100644 index 000000000..6c6e29422 --- /dev/null +++ b/libc/inet/sockatmark.c @@ -0,0 +1,38 @@ +/* + * Copyright © 2005-2020 Rich Felker, et al. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Imported from musl C library + */ + +#include <sys/ioctl.h> +#include <sys/socket.h> + +/* Determine whether socket is at a out-of-band mark. */ +int sockatmark(int fd) +{ + int ret; + + if (ioctl(fd, SIOCATMARK, &ret) < 0) + return -1; + + return ret; +} diff --git a/libc/inet/socketcalls.c b/libc/inet/socketcalls.c index edb5ab87e..774ed6091 100644 --- a/libc/inet/socketcalls.c +++ b/libc/inet/socketcalls.c @@ -10,6 +10,10 @@ #include <cancel.h> #include <bits/kernel-features.h> +#if defined(__UCLIBC_USE_TIME64__) +#include "internal/time64_helpers.h" +#endif + #ifdef __NR_socketcall /* Various socketcall numbers */ #define SYS_SOCKET 1 @@ -272,7 +276,9 @@ lt_libc_hidden(recvmsg) static ssize_t __NC(recvmmsg)(int sockfd, struct mmsghdr *msg, size_t vlen, int flags, struct timespec *tmo) { -# ifdef __NR_recvmmsg +# if defined(__UCLIBC_USE_TIME64__) && defined(__NR_recvmmsg_time64) + return (ssize_t)INLINE_SYSCALL(recvmmsg_time64, 5, sockfd, msg, vlen, flags, TO_TS64_P(tmo)); +# elif defined(__NR_recvmmsg) return (ssize_t)INLINE_SYSCALL(recvmmsg, 5, sockfd, msg, vlen, flags, tmo); # elif __NR_socketcall unsigned long args[5]; |