/* Copyright (C) 2020 C. McEnroe * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Additional permission under GNU GPL version 3 section 7: * * If you modify this Program, or any covered work, by linking or * combining it with OpenSSL (or a modified version of that library), * containing parts covered by the terms of the OpenSSL License and the * original SSLeay license, the licensors of this Program grant you * additional permission to convey the resulting work. Corresponding * Source for a non-source form of such a combination shall include the * source code for the parts of OpenSSL used as well as that of the * covered work. */ #include #include #include #include #include #include #include #ifndef EAI_BADHINTS #define EAI_BADHINTS EAI_BADFLAGS #endif #ifndef EAI_PROTOCOL #define EAI_PROTOCOL EAI_BADFLAGS #endif /* A wrapper around getaddrinfo(3) which first performs SRV record (RFC 2782) * lookup. hints must be provided and hints->ai_protocol must be set. SRV * lookup is skipped if servname is numerical. If SRV lookup is successful, the * ai_canonname field of the first addrinfo structure returned is set to the * target name. Only the first SRV record is used. Priority and weight are * ignored. */ int getservinfo( const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res ) { if (!hints) return EAI_BADHINTS; if (!hints->ai_protocol) return EAI_PROTOCOL; char *rest; strtoul(servname, &rest, 10); if (!*rest || hints->ai_flags & (AI_NUMERICHOST | AI_NUMERICSERV)) { return getaddrinfo(hostname, servname, hints, res); } struct protoent *proto = getprotobynumber(hints->ai_protocol); if (!proto) return EAI_PROTOCOL; char dname[256]; int len = snprintf( dname, sizeof(dname), "_%s._%s.%s", servname, proto->p_name, hostname ); if ((size_t)len >= sizeof(dname)) return EAI_OVERFLOW; uint8_t msg[512]; len = res_query(dname, 1 /* IN */, 33 /* SRV */, msg, sizeof(msg)); if (len < 12) return getaddrinfo(hostname, servname, hints, res); uint8_t *ptr = &msg[12]; #define NAME_SKIP \ for (uint8_t n; ptr < &msg[len] && (n = *ptr++); ptr += n) { \ if (n & 0xC0) { \ ptr++; \ break; \ } \ } uint16_t qdcount = msg[4] << 8 | msg[5]; for (uint16_t q = 0; ptr < &msg[len] && q < qdcount; ++q) { NAME_SKIP; // QNAME ptr += 4; // QTYPE, QCLASS } NAME_SKIP; // NAME ptr += 14; // TYPE, CLASS, TTL, RDLENGTH, Priority, Weight if (&msg[len] < ptr + 3) { return getaddrinfo(hostname, servname, hints, res); } char port[sizeof("65535")]; snprintf(port, sizeof(port), "%d", ptr[0] << 8 | ptr[1]); ptr += 2; // Name compression is not used for Target. if (!ptr[0]) return EAI_NONAME; hostname = (const char *)&ptr[1]; for (uint8_t n; ptr < &msg[len] && (n = *ptr); ptr += n) { *ptr++ = '.'; } struct addrinfo myHints = *hints; myHints.ai_flags |= AI_NUMERICSERV; myHints.ai_flags &= ~AI_CANONNAME; int error = getaddrinfo(hostname, port, &myHints, res); if (error) return error; (*res)->ai_canonname = strdup(hostname); if (!(*res)->ai_canonname) { freeaddrinfo(*res); return EAI_MEMORY; } return 0; }