Patch by Robert Scheck <robert@fedoraproject.org>, based on a patch by Matěj Cepl <mcepl@redhat.com> for bitlbee >= 1.2, to avoid static linking to a private glibc function, normally visible as libc.so.6(GLIBC_PRIVATE) in the binary RPM package. Glibc >= 2.9 allows dynamic linking using -lresolv for ns_initparse()/ns_parserr(), but Red Hat Enterprise Linux 4 and 5 (including derivates) are shipping older glibc versions. A few more information and details regarding this patch are mentioned in Red Hat Bugzilla ID #439047: https://bugzilla.redhat.com/show_bug.cgi?id=439047 --- bitlbee-1.2.7/configure 2010-04-19 15:10:03.000000000 +0200 +++ bitlbee-1.2.7/configure.libresolv 2010-04-25 14:52:00.000000000 +0200 @@ -19,7 +19,7 @@ pidfile='/var/run/bitlbee.pid' ipcsocket='/var/run/bitlbee.sock' pcdir='$prefix/lib/pkgconfig' -systemlibdirs="/lib /lib64 /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64" +systemlibdirs="/lib64 /usr/lib64 /usr/local/lib64 /lib /usr/lib /usr/local/lib" msn=1 jabber=1 @@ -313,10 +313,10 @@ TMPFILE=$(mktemp /tmp/bitlbee-configure.XXXXXX) ret=1 for i in $systemlibdirs; do - if [ -f $i/libresolv.a ]; then + if [ -f $i/libresolv.so ]; then echo "$RESOLV_TESTCODE" | $CC -o $TMPFILE -x c - -Wl,$i/libresolv.a >/dev/null 2>/dev/null if [ "$?" = "0" ]; then - echo 'EFLAGS+='$i'/libresolv.a' >> Makefile.settings + echo 'EFLAGS+=-lresolv' >> Makefile.settings ret=0 fi fi --- bitlbee-1.2.7/lib/Makefile 2010-04-19 15:10:03.000000000 +0200 +++ bitlbee-1.2.7/lib/Makefile.libresolv 2010-04-25 14:52:00.000000000 +0200 @@ -9,7 +9,7 @@ -include ../Makefile.settings # [SH] Program variables -objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o oauth.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o +objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o oauth.o proxy.o sha1.o srv.o $(SSL_CLIENT) url.o xmltree.o CFLAGS += -Wall LFLAGS += -r --- bitlbee-1.2.7/lib/misc.c 2010-04-19 15:10:03.000000000 +0200 +++ bitlbee-1.2.7/lib/misc.c.libresolv 2010-04-25 14:58:57.000000000 +0200 @@ -44,6 +44,7 @@ #ifdef HAVE_RESOLV_A #include <arpa/nameser.h> #include <resolv.h> +#include <netinet/in.h> #endif #include "md5.h" @@ -505,58 +506,6 @@ return 0; } -struct ns_srv_reply *srv_lookup( char *service, char *protocol, char *domain ) -{ - struct ns_srv_reply *reply = NULL; -#ifdef HAVE_RESOLV_A - char name[1024]; - unsigned char querybuf[1024]; - const unsigned char *buf; - ns_msg nsh; - ns_rr rr; - int i, len, size; - - g_snprintf( name, sizeof( name ), "_%s._%s.%s", service, protocol, domain ); - - if( ( size = res_query( name, ns_c_in, ns_t_srv, querybuf, sizeof( querybuf ) ) ) <= 0 ) - return NULL; - - if( ns_initparse( querybuf, size, &nsh ) != 0 ) - return NULL; - - if( ns_parserr( &nsh, ns_s_an, 0, &rr ) != 0 ) - return NULL; - - size = ns_rr_rdlen( rr ); - buf = ns_rr_rdata( rr ); - - len = 0; - for( i = 6; i < size && buf[i]; i += buf[i] + 1 ) - len += buf[i] + 1; - - if( i > size ) - return NULL; - - reply = g_malloc( sizeof( struct ns_srv_reply ) + len ); - memcpy( reply->name, buf + 7, len ); - - for( i = buf[6]; i < len && buf[7+i]; i += buf[7+i] + 1 ) - reply->name[i] = '.'; - - if( i > len ) - { - g_free( reply ); - return NULL; - } - - reply->prio = ( buf[0] << 8 ) | buf[1]; - reply->weight = ( buf[2] << 8 ) | buf[3]; - reply->port = ( buf[4] << 8 ) | buf[5]; -#endif - - return reply; -} - /* Word wrapping. Yes, I know this isn't UTF-8 clean. I'm willing to take the risk. */ char *word_wrap( const char *msg, int line_len ) { --- bitlbee-1.2.7/lib/misc.h 2010-04-19 15:10:03.000000000 +0200 +++ bitlbee-1.2.7/lib/misc.h.libresolv 2010-04-25 14:56:02.000000000 +0200 @@ -28,14 +28,7 @@ #include <gmodule.h> #include <time.h> - -struct ns_srv_reply -{ - int prio; - int weight; - int port; - char name[]; -}; +#include "srv.h" G_MODULE_EXPORT void strip_linefeed( gchar *text ); G_MODULE_EXPORT char *add_cr( char *text ); --- bitlbee-1.2.7/lib/srv.c 1970-01-01 01:00:00.000000000 +0100 +++ bitlbee-1.2.7/lib/srv.c.libresolv 2010-04-25 14:58:24.000000000 +0200 @@ -0,0 +1,249 @@ +/* srv.c - DNS SRV code + * Copyright (C) 2003 Free Software Foundation, Inc. + * + * This file is part of GNUPG. + * + * GNUPG 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. + * + * GNUPG 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 <http://www.gnu.org/licenses/>. + */ + +#undef TEST + +#ifdef TEST +#define HAVE_RESOLV_A +#endif + +#include <config.h> +#include <sys/types.h> + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#ifdef HAVE_RESOLV_A +# ifdef _WIN32 +# include <windows.h> +# else +# include <netinet/in.h> +# include <arpa/nameser.h> +# include <resolv.h> +# endif /* !_WIN32 */ +#endif /* USE_DNS_SRV */ + +#include "srv.h" +#include <glib.h> +#include <glib/gprintf.h> +#include <glib/gutils.h> + +/* Not every installation has gotten around to supporting SRVs yet...*/ +#ifndef T_SRV +#define T_SRV 33 +#endif + +static int priosort(const void *a, const void *b) { + const struct ns_srv_reply *sa=a, *sb=b; + if (sa->prio>sb->prio) + return 1; + else if (sa->prio<sb->prio) + return -1; + else + return 0; +} + +struct ns_srv_reply *srv_lookup(char *service, char *protocol, char *domain) { + struct ns_srv_reply *reply = NULL; + struct ns_srv_reply *list = NULL; + char name[1024]; +#ifdef HAVE_RESOLV_A + unsigned char answer[PACKETSZ]; + int r, srvcount=0; + unsigned char *pt, *emsg; + int count, dlen; + + g_snprintf(name, sizeof(name ), "_%s._%s.%s", service, protocol, domain); + + _res.options |= RES_DEBUG; + + if ( res_init() != 0 ) + return NULL; + + r=res_query(name, C_IN, T_SRV, answer, PACKETSZ); + + if (r<sizeof(HEADER) || r>PACKETSZ) + return NULL; + + if ((((HEADER *)answer)->rcode)==NOERROR && (count=ntohs(((HEADER *)answer)->ancount))) { + int i, rc; + + emsg=&answer[r]; /* end of message ??? */ + pt=&answer[sizeof(HEADER)]; + + /* Skip over the query */ + rc=dn_skipname(pt, emsg); + if (rc==-1) + goto fail; + + pt+=rc+QFIXEDSZ; + + while (count-->0 && pt<emsg) { + struct ns_srv_reply *srv=NULL; + int type, class; + + list=g_realloc(list, (srvcount+1)*sizeof(struct ns_srv_reply)); + memset(&list[srvcount], 0, sizeof(struct ns_srv_reply)); + srv=&list[srvcount]; + srvcount++; + + rc=dn_skipname(pt, emsg); /* the name we just queried for */ + if (rc==-1) + goto fail; + pt+=rc; + + /* Truncated message? */ + if ((emsg-pt)<16) + goto fail; + + type=*pt++ << 8; + type|=*pt++; + /* We asked for SRV and got something else !? */ + if (type!=T_SRV) + goto fail; + + class=*pt++ << 8; + class|=*pt++; + /* We asked for IN and got something else !? */ + if (class!=C_IN) + goto fail; + + pt+=4; /* ttl */ + dlen=*pt++ << 8; + dlen|=*pt++; + srv->prio=*pt++ << 8; + srv->prio|=*pt++; + srv->weight=*pt++ << 8; + srv->weight|=*pt++; + srv->port=*pt++ << 8; + srv->port|=*pt++; + + /* Get the name. 2782 doesn't allow name compression, but + dn_expand still works to pull the name out of the + packet. */ + rc=dn_expand(answer, emsg, pt, srv->name, MAXDNAME); + if (rc==1 && srv->name[0]==0) /* "." */ + goto noanswer; + if (rc==-1) + goto fail; + pt+=rc; + /* Corrupt packet? */ + if (dlen!=rc+6) + goto fail; + } + + /* Now we have an array of all the srv records. */ + + /* Order by priority */ + qsort(list, srvcount, sizeof(struct ns_srv_reply), priosort); + + /* For each priority, move the zero-weighted items first. */ + for (i=0; i<srvcount; i++) { + int j; + + for (j=i; j<srvcount && list[i].prio==list[j].prio; j++) { + if (list[j].weight==0) { + /* Swap j with i */ + if (j!=i) { + struct ns_srv_reply temp; + + memcpy(&temp, &list[j], sizeof(struct ns_srv_reply)); + memcpy(&list[j], &list[i], sizeof(struct ns_srv_reply)); + memcpy(&list[i], &temp, sizeof(struct ns_srv_reply)); + } + + break; + } + } + } + + /* Run the RFC-2782 weighting algorithm. We don't need very + high quality randomness for this, so regular libc srand/rand + is sufficient. */ + srand(time(NULL)*getpid()); + + for (i=0; i<srvcount; i++) { + int j; + float prio_count=0, chose; + + for (j=i; j<srvcount && list[i].prio==list[j].prio; j++) { + prio_count+=list[j].weight; + list[j].run_count=prio_count; + } + + chose=prio_count*rand()/RAND_MAX; + + for (j=i; j<srvcount && list[i].prio==list[j].prio; j++) { + if (chose<=list[j].run_count) { + /* Swap j with i */ + if (j!=i) { + struct ns_srv_reply temp; + + memcpy(&temp, &list[j], sizeof(struct ns_srv_reply)); + memcpy(&list[j], &list[i], sizeof(struct ns_srv_reply)); + memcpy(&list[i], &temp, sizeof(struct ns_srv_reply)); + } + break; + } + } + } + } + + if (list == NULL) + return NULL; + + /* TODO: bitlbee should expect a situation when more than one server + * is available for the given SRV record. + */ + reply = g_malloc(sizeof(struct ns_srv_reply)); + *reply = *list; + g_free(list); + /* list=NULL; */ +#endif /* HAVE_RESOLV_A */ + return reply; + + noanswer: g_free(list); + list=NULL; + return NULL; + + fail: g_free(list); + list=NULL; + return NULL; +} + +#ifdef TEST +int main(int argc, char *argv[]) { + struct ns_srv_reply *srv; + /*int rc,i;*/ + + srv=srv_lookup("xmpp-client", "tcp", "jabber.org"); + if (srv != NULL) { + printf("priority=%hu\n", srv->prio); + printf("weight=%hu\n", srv->weight); + printf("port=%hu\n", srv->port); + printf("target=%s\n", srv->name); + printf("\n"); + g_free(srv); + } + + return 0; +} +#endif /* TEST */ --- bitlbee-1.2.7/lib/srv.h 1970-01-01 01:00:00.000000000 +0100 +++ bitlbee-1.2.7/lib/srv.h.libresolv 2010-04-25 14:58:44.000000000 +0200 @@ -0,0 +1,38 @@ +/* srv.h + * Copyright (C) 2003, 2004 Free Software Foundation, Inc. + * + * This file is part of GNUPG. + * + * GNUPG 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. + * + * GNUPG 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_SRV_H +#define GNUPG_COMMON_SRV_H + +#ifndef MAXDNAME +#define MAXDNAME 1025 +#endif + +struct ns_srv_reply +{ + int prio; /* priority */ + int weight; /* weight */ + int port; /* port */ + int run_count; /* from struct srventry at gnupg's common/srv.h */ + char name[MAXDNAME]; /* target */ +}; + +struct ns_srv_reply *srv_lookup(char *service, char *protocol, char *domain); + +#endif /*GNUPG_COMMON_SRV_H*/ --- bitlbee-1.2.7/Makefile 2010-04-19 15:10:03.000000000 +0200 +++ bitlbee-1.2.7/Makefile.libresolv 2010-04-25 14:52:00.000000000 +0200 @@ -10,7 +10,7 @@ # Program variables objects = account.o bitlbee.o chat.o crypting.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) user.o -headers = account.h bitlbee.h commands.h conf.h config.h crypting.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/nogaim.h +headers = account.h bitlbee.h commands.h conf.h config.h crypting.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/srv.h lib/ssl_client.h lib/url.h protocols/nogaim.h subdirs = lib protocols ifeq ($(TARGET),i586-mingw32msvc)