/* 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 static uint16_t u16(uint8_t **ptr) { uint16_t x = *(*ptr)++; x = x << 8 | *(*ptr)++; return x; } static void nameSkip(uint8_t **ptr) { for (uint8_t len; (len = *(*ptr)++); *ptr += len) { if (len & 0xC0) { (*ptr)++; break; } } } static char *nameString(uint8_t **ptr) { char *name = (char *)(*ptr + 1); for (uint8_t len; (len = **ptr); *ptr += len) { *(*ptr)++ = '.'; } return name; } /* 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. */ 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; if (hints->ai_flags & AI_NUMERICHOST) return EAI_BADFLAGS; if (hints->ai_flags & AI_NUMERICSERV) return EAI_BADFLAGS; char *rest; strtoul(servname, &rest, 10); if (!*rest) 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 < 0) return getaddrinfo(hostname, servname, hints, res); uint8_t *ptr = msg; u16(&ptr); // ID uint16_t rcode = u16(&ptr) & 0x000F; uint16_t qdcount = u16(&ptr); uint16_t ancount = u16(&ptr); u16(&ptr); // NSCOUNT u16(&ptr); // ARCOUNT if (rcode || !ancount) return getaddrinfo(hostname, servname, hints, res); for (uint16_t q = 0; q < qdcount; ++q) { nameSkip(&ptr); // QNAME u16(&ptr); // QTYPE u16(&ptr); // QCLASS } // Read only one answer, ignoring Priority and Weight. Does anyone actually // use them? nameSkip(&ptr); // NAME u16(&ptr); // TYPE u16(&ptr); // CLASS u16(&ptr); // TTL u16(&ptr); // TTL u16(&ptr); // RDLENGTH u16(&ptr); // Priority u16(&ptr); // Weight uint16_t port = u16(&ptr); hostname = nameString(&ptr); if (!hostname[0]) return EAI_NONAME; char myServ[sizeof("65535")]; snprintf(myServ, sizeof(myServ), "%hu", port); struct addrinfo myHints = *hints; #ifdef AI_DEFAULT if (!myHints.ai_flags) myHints.ai_flags = AI_DEFAULT; #endif myHints.ai_flags |= AI_NUMERICSERV; myHints.ai_flags &= ~AI_CANONNAME; int error = getaddrinfo(hostname, myServ, &myHints, res); if (error) return error; (*res)->ai_canonname = strdup(hostname); if (!(*res)->ai_canonname) { freeaddrinfo(*res); return EAI_MEMORY; } return 0; }