]> git.saurik.com Git - apt.git/commitdiff
Merge branch 'debian/experimental' into feature/srv-records
authorMichael Vogt <mvo@debian.org>
Tue, 18 Aug 2015 09:54:05 +0000 (11:54 +0200)
committerMichael Vogt <mvo@debian.org>
Tue, 18 Aug 2015 09:54:05 +0000 (11:54 +0200)
Conflicts:
cmdline/apt-helper.cc
cmdline/makefile

apt-pkg/contrib/srvrec.cc [new file with mode: 0644]
apt-pkg/contrib/srvrec.h [new file with mode: 0644]
apt-pkg/makefile
cmdline/apt-helper.cc
cmdline/makefile
methods/connect.cc
methods/makefile

diff --git a/apt-pkg/contrib/srvrec.cc b/apt-pkg/contrib/srvrec.cc
new file mode 100644 (file)
index 0000000..70247c2
--- /dev/null
@@ -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 (file)
index 0000000..79b318b
--- /dev/null
@@ -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
index 5603b51ed82216666e66c3b2ea2e7715925fe2f3..45dcddfe34371b84ddc6ae298641ca2308a28128 100644 (file)
@@ -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
index a97fc903f35f33bf225621cbd6a22b0bd34dde7f..482e64dd15cff48980187cfcee2d8e33481ca866 100644 (file)
@@ -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}};
 
index 816038c3b381045148fff4c0c814568a7768a0c0..6d21b08031ab34d5bc1d20ec29240adb741e791e 100644 (file)
@@ -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)
index e2cbf4f5ce2977c65e210adbc7499cc00db52696..8de4ad74799d29715bee12d8e97ad670d20b7e2b 100644 (file)
@@ -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;
+}
index 6b77812943cbe47a0d807eb9ce070829a7026e6d..868c52a402d3cb85cdf361afcef21c1663eaf248 100644 (file)
@@ -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)