diff options
-rw-r--r-- | apt-pkg/contrib/srvrec.cc | 141 | ||||
-rw-r--r-- | apt-pkg/contrib/srvrec.h | 42 | ||||
-rw-r--r-- | apt-pkg/makefile | 2 | ||||
-rw-r--r-- | cmdline/apt-helper.cc | 25 | ||||
-rw-r--r-- | cmdline/makefile | 2 | ||||
-rw-r--r-- | methods/connect.cc | 52 | ||||
-rw-r--r-- | methods/makefile | 8 |
7 files changed, 258 insertions, 14 deletions
diff --git a/apt-pkg/contrib/srvrec.cc b/apt-pkg/contrib/srvrec.cc new file mode 100644 index 000000000..70247c2ae --- /dev/null +++ b/apt-pkg/contrib/srvrec.cc @@ -0,0 +1,141 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +/* ###################################################################### + + SRV record support + + ##################################################################### */ + /*}}}*/ +#include <config.h> + +#include <netdb.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv.h> + +#include <algorithm> + +#include <apt-pkg/strutl.h> +#include <apt-pkg/error.h> +#include "srvrec.h" + +bool GetSrvRecords(std::string host, int port, std::vector<SrvRec> &Result) +{ + std::string target; + struct servent *s_ent = getservbyport(htons(port), "tcp"); + if (s_ent == NULL) + return false; + + strprintf(target, "_%s._tcp.%s", s_ent->s_name, host.c_str()); + return GetSrvRecords(target, Result); +} + +bool GetSrvRecords(std::string name, std::vector<SrvRec> &Result) +{ + unsigned char answer[PACKETSZ]; + int answer_len, compressed_name_len; + int answer_count; + + if (res_init() != 0) + return _error->Errno("res_init", "Failed to init resolver"); + + answer_len = res_query(name.c_str(), C_IN, T_SRV, answer, sizeof(answer)); + if (answer_len < (int)sizeof(HEADER)) + return _error->Warning("Not enough data from res_query (%i)", answer_len); + + // check the header + HEADER *header = (HEADER*)answer; + if (header->rcode != NOERROR) + return _error->Warning("res_query returned rcode %i", header->rcode); + answer_count = ntohs(header->ancount); + if (answer_count <= 0) + return _error->Warning("res_query returned no answers (%i) ", answer_count); + + // skip the header + compressed_name_len = dn_skipname(answer+sizeof(HEADER), answer+answer_len); + if(compressed_name_len < 0) + return _error->Warning("dn_skipname failed %i", compressed_name_len); + + // pt points to the first answer record, go over all of them now + unsigned char *pt = answer+sizeof(HEADER)+compressed_name_len+QFIXEDSZ; + while ((int)Result.size() < answer_count && pt < answer+answer_len) + { + SrvRec rec; + u_int16_t type, klass, priority, weight, port, dlen; + char buf[MAXDNAME]; + + compressed_name_len = dn_skipname(pt, answer+answer_len); + if (compressed_name_len < 0) + return _error->Warning("dn_skipname failed (2): %i", + compressed_name_len); + pt += compressed_name_len; + if (((answer+answer_len) - pt) < 16) + return _error->Warning("packet too short"); + + // extract the data out of the result buffer + #define extract_u16(target, p) target = *p++ << 8; target |= *p++; + + extract_u16(type, pt); + if(type != T_SRV) + return _error->Warning("Unexpected type excepted %x != %x", + T_SRV, type); + extract_u16(klass, pt); + if(klass != C_IN) + return _error->Warning("Unexpected class excepted %x != %x", + C_IN, klass); + pt += 4; // ttl + extract_u16(dlen, pt); + extract_u16(priority, pt); + extract_u16(weight, pt); + extract_u16(port, pt); + + #undef extract_u16 + + compressed_name_len = dn_expand(answer, answer+answer_len, pt, buf, sizeof(buf)); + if(compressed_name_len < 0) + return _error->Warning("dn_expand failed %i", compressed_name_len); + pt += compressed_name_len; + + // add it to our class + rec.priority = priority; + rec.weight = weight; + rec.port = port; + rec.target = buf; + Result.push_back(rec); + } + + // implement load balancing as specified in RFC-2782 + + // sort them by priority + std::stable_sort(Result.begin(), Result.end()); + + // assign random number ranges + int prev_weight = 0; + int prev_priority = 0; + for(std::vector<SrvRec>::iterator I = Result.begin(); + I != Result.end(); ++I) + { + if(prev_priority != I->priority) + prev_weight = 0; + I->random_number_range_start = prev_weight; + I->random_number_range_end = prev_weight + I->weight; + prev_weight = I->random_number_range_end; + prev_priority = I->priority; + } + + // go over the code in reverse order and note the max random range + int max = 0; + prev_priority = 0; + for(std::vector<SrvRec>::iterator I = Result.end(); + I != Result.begin(); --I) + { + if(prev_priority != I->priority) + max = I->random_number_range_end; + I->random_number_range_max = max; + } + + // now shuffle + + return true; +} diff --git a/apt-pkg/contrib/srvrec.h b/apt-pkg/contrib/srvrec.h new file mode 100644 index 000000000..79b318b48 --- /dev/null +++ b/apt-pkg/contrib/srvrec.h @@ -0,0 +1,42 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +/* ###################################################################### + + SRV record support + + ##################################################################### */ + /*}}}*/ +#ifndef SRVREC_H +#define SRVREC_H + +#include <arpa/nameser.h> +#include <vector> +#include <string> + +class SrvRec +{ + public: + std::string target; + u_int16_t priority; + u_int16_t weight; + u_int16_t port; + + // each server is assigned a interval [start, end] in the space of [0, max] + int random_number_range_start; + int random_number_range_end; + int random_number_range_max; + + bool operator<(SrvRec const &other) const { + return this->priority < other.priority; + } +}; + +/** \brief Get SRV records from host/port (builds the query string internally) + */ +bool GetSrvRecords(std::string name, std::vector<SrvRec> &Result); + +/** \brief Get SRV records for query string like: _http._tcp.example.com + */ +bool GetSrvRecords(std::string host, int port, std::vector<SrvRec> &Result); + +#endif diff --git a/apt-pkg/makefile b/apt-pkg/makefile index 5603b51ed..45dcddfe3 100644 --- a/apt-pkg/makefile +++ b/apt-pkg/makefile @@ -15,7 +15,7 @@ include ../buildlib/libversion.mak LIBRARY=apt-pkg MAJOR=$(LIBAPTPKG_MAJOR) MINOR=$(LIBAPTPKG_RELEASE) -SLIBS=$(PTHREADLIB) $(INTLLIBS) -lutil -ldl +SLIBS=$(PTHREADLIB) $(INTLLIBS) -lutil -ldl -lresolv ifeq ($(HAVE_ZLIB),yes) SLIBS+= -lz endif diff --git a/cmdline/apt-helper.cc b/cmdline/apt-helper.cc index a97fc903f..482e64dd1 100644 --- a/cmdline/apt-helper.cc +++ b/cmdline/apt-helper.cc @@ -22,6 +22,7 @@ #include <apt-private/private-output.h> #include <apt-private/private-download.h> #include <apt-private/private-cmndline.h> +#include <apt-pkg/srvrec.h> #include <iostream> #include <string> @@ -81,6 +82,29 @@ static bool DoDownloadFile(CommandLine &CmdL) return true; } +static bool DoSrvLookup(CommandLine &CmdL) +{ + if (CmdL.FileSize() < 1) + return _error->Error(_("Must specifc at least one srv record")); + + std::vector<SrvRec> srv_records; + for(int i=1; CmdL.FileList[i] != NULL; i++) + { + if(GetSrvRecords(CmdL.FileList[i], srv_records) == false) + _error->Warning(_("GetSrvRec failed for %s"), CmdL.FileList[i]); + for (std::vector<SrvRec>::const_iterator I = srv_records.begin(); + I != srv_records.end(); ++I) + { + c1out << (*I).target.c_str() << " " + << (*I).priority << " " + << (*I).weight << " " + << (*I).port << " " + << std::endl; + } + } + return true; +} + static bool ShowHelp(CommandLine &) { ioprintf(std::cout, "%s %s (%s)\n", PACKAGE, PACKAGE_VERSION, COMMON_ARCH); @@ -107,6 +131,7 @@ int main(int argc,const char *argv[]) /*{{{*/ { CommandLine::Dispatch Cmds[] = {{"help",&ShowHelp}, {"download-file", &DoDownloadFile}, + {"srv-lookup", &DoSrvLookup}, {"auto-detect-proxy", &DoAutoDetectProxy}, {0,0}}; diff --git a/cmdline/makefile b/cmdline/makefile index 816038c3b..6d21b0803 100644 --- a/cmdline/makefile +++ b/cmdline/makefile @@ -49,7 +49,7 @@ include $(PROGRAM_H) # The apt-helper PROGRAM=apt-helper -SLIBS = -lapt-pkg -lapt-private $(INTLLIBS) +SLIBS = -lapt-pkg -lapt-private $(INTLLIBS) -lresolv LIB_MAKES = apt-pkg/makefile apt-private/makefile SOURCE = apt-helper.cc include $(PROGRAM_H) diff --git a/methods/connect.cc b/methods/connect.cc index e2cbf4f5c..8de4ad747 100644 --- a/methods/connect.cc +++ b/methods/connect.cc @@ -18,6 +18,7 @@ #include <apt-pkg/strutl.h> #include <apt-pkg/acquire-method.h> #include <apt-pkg/configuration.h> +#include <apt-pkg/srvrec.h> #include <stdio.h> #include <errno.h> @@ -43,6 +44,9 @@ static int LastPort = 0; static struct addrinfo *LastHostAddr = 0; static struct addrinfo *LastUsed = 0; +static std::vector<SrvRec> SrvRecords; +static int LastSrvRecord = 0; + // Set of IP/hostnames that we timed out before or couldn't resolve static std::set<std::string> bad_addr; @@ -130,15 +134,12 @@ static bool DoConnect(struct addrinfo *Addr,std::string Host, return true; } /*}}}*/ -// Connect - Connect to a server /*{{{*/ -// --------------------------------------------------------------------- -/* Performs a connection to the server */ -bool Connect(std::string Host,int Port,const char *Service,int DefPort,int &Fd, - unsigned long TimeOut,pkgAcqMethod *Owner) -{ - if (_error->PendingError() == true) - return false; +// Connect to a given Hostname +bool ConnectAfterSrvRecords(std::string Host,int Port,const char *Service, + int DefPort,int &Fd, + unsigned long TimeOut,pkgAcqMethod *Owner) +{ // Convert the port name/number char ServStr[300]; if (Port != 0) @@ -258,3 +259,38 @@ bool Connect(std::string Host,int Port,const char *Service,int DefPort,int &Fd, return _error->Error(_("Unable to connect to %s:%s:"),Host.c_str(),ServStr); } /*}}}*/ +// Connect - Connect to a server /*{{{*/ +// --------------------------------------------------------------------- +/* Performs a connection to the server */ +bool Connect(std::string Host,int Port,const char *Service, + int DefPort,int &Fd, + unsigned long TimeOut,pkgAcqMethod *Owner) +{ +#if 0 + if (_error->PendingError() == true) + return false; +#endif + + if(LastHost != Host || LastPort != Port) + { + SrvRecords.clear(); + bool res = GetSrvRecords(Host, DefPort, SrvRecords); + } + if(SrvRecords.size() == 0) + return ConnectAfterSrvRecords(Host, Port, Service, DefPort, Fd, + TimeOut, Owner); + + bool connected = false; + while(SrvRecords.size() > 0) + { + Host = SrvRecords[0].target; + connected = ConnectAfterSrvRecords(Host, Port, Service, DefPort, Fd, + TimeOut, Owner); + if(connected == true) + return true; + + // we couldn't connect to this one, use the next + SrvRecords.erase(SrvRecords.begin()); + } + return false; +} diff --git a/methods/makefile b/methods/makefile index 6b7781294..868c52a40 100644 --- a/methods/makefile +++ b/methods/makefile @@ -46,21 +46,21 @@ include $(PROGRAM_H) # The http method PROGRAM=http -SLIBS = -lapt-pkg $(SOCKETLIBS) $(INTLLIBS) +SLIBS = -lapt-pkg $(SOCKETLIBS) $(INTLLIBS) -lresolv LIB_MAKES = apt-pkg/makefile SOURCE = http.cc http_main.cc rfc2553emu.cc connect.cc server.cc include $(PROGRAM_H) # The https method PROGRAM=https -SLIBS = -lapt-pkg -lcurl $(INTLLIBS) +SLIBS = -lapt-pkg -lcurl $(INTLLIBS) -lresolv LIB_MAKES = apt-pkg/makefile SOURCE = https.cc server.cc include $(PROGRAM_H) # The ftp method PROGRAM=ftp -SLIBS = -lapt-pkg $(SOCKETLIBS) $(INTLLIBS) +SLIBS = -lapt-pkg $(SOCKETLIBS) $(INTLLIBS) -lresolv LIB_MAKES = apt-pkg/makefile SOURCE = ftp.cc rfc2553emu.cc connect.cc include $(PROGRAM_H) @@ -81,7 +81,7 @@ include $(PROGRAM_H) # The mirror method PROGRAM=mirror -SLIBS = -lapt-pkg $(SOCKETLIBS) +SLIBS = -lapt-pkg $(SOCKETLIBS) -lresolv LIB_MAKES = apt-pkg/makefile SOURCE = mirror.cc http.cc rfc2553emu.cc connect.cc server.cc include $(PROGRAM_H) |