Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -30,10 +30,41 @@ AS_IF([test x"$enable_static" = x"yes" -o x"$enable_shared" = x"no"], [ AC_PATH_TOOL(AR, ar) AC_PROG_RANLIB AC_SUBST(OBJXMPP_STATIC_LIB, "libobjxmpp.a") ]) + +# This is an adapted version of what glib does for res_query +# It should recognize the correct library on (at least) Linux, +# NetBSD, FreeBSD, Mac OS X and Haiku +AC_MSG_CHECKING([for res_nsearch]) +AC_TRY_LINK([#include + #include + #include + #include ], + [res_nsearch(&_res, "test", 0, 0, (void *)0, 0);], + [AC_MSG_RESULT([yes])], + [save_libs="$LIBS" + LIBS="$LIBS -lresolv" + AC_TRY_LINK([#include + #include + #include + #include ], + [res_nsearch(&_res, "test", 0, 0, (void *)0, 0);], + [AC_MSG_RESULT([in -lresolv])], + [LIBS="$save_libs -lnetwork" + AC_TRY_LINK([#include + #include + #include + #include ], + [res_nsearch(&_res, "test", 0, 0, (void *)0, 0);], + [AC_MSG_RESULT([in -lnetwork])], + [LIBS="$save_libs -lbind" + AC_TRY_LINK([#include ], + [res_nsearch(&_res, "test", 0, 0, (void *)0, 0);], + [AC_MSG_RESULT([in -lbind])], + [AC_MSG_ERROR(not found)])])])]) AC_CHECK_LIB(objopenssl, main, [ LIBS="$LIBS -lobjopenssl -lcrypto" ], [ AC_MSG_ERROR(You need ObjOpenSSL installed!) Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -14,14 +14,15 @@ XMPPPLAINAuth.m \ XMPPPresence.m \ XMPPRoster.m \ XMPPRosterItem.m \ XMPPSCRAMAuth.m \ + XMPPSRVEnumerator.m \ XMPPStanza.m INCLUDES = ${SRCS:.m=.h} \ namespaces.h \ ObjXMPP.h include ../buildsys.mk LD = ${OBJC} Index: src/XMPPConnection.m ================================================================== --- src/XMPPConnection.m +++ src/XMPPConnection.m @@ -29,10 +29,11 @@ #include #import #import "XMPPConnection.h" +#import "XMPPSRVEnumerator.h" #import "XMPPSCRAMAuth.h" #import "XMPPPLAINAuth.h" #import "XMPPStanza.h" #import "XMPPJID.h" #import "XMPPIQ.h" @@ -194,13 +195,31 @@ } - (void)connect { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + XMPPSRVEnumerator *SRVEnumerator = + [XMPPSRVEnumerator enumeratorWithDomain: server]; + XMPPSRVEntry *candidate; - [sock connectToHost: server - onPort: port]; + while ((candidate = [SRVEnumerator nextObject])) { + @try { + [sock connectToHost: [candidate target] + onPort: [candidate port]]; + break; + } @catch (id e) { + if (([e class] == [OFAddressTranslationFailedException + class]) || ([e class] == + [OFConnectionFailedException class])) + continue; + else + @throw e; + } + } + if (!candidate) + [sock connectToHost: server + onPort: port]; [self XMPP_startStream]; [pool release]; } ADDED src/XMPPSRVEnumerator.h Index: src/XMPPSRVEnumerator.h ================================================================== --- src/XMPPSRVEnumerator.h +++ src/XMPPSRVEnumerator.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2011, Florian Zeitz + * + * https://webkeks.org/hg/objxmpp/ + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice is present in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#import + +@interface XMPPSRVEntry: OFObject +{ + uint16_t priority; + uint16_t weight; + uint16_t port; + OFString *target; +} +#ifdef OF_HAVE_PROPERTIES +@property uint16_t priority; +@property uint16_t weight; +@property uint16_t port; +@property (copy) OFString *target; +#endif + +- (void) setPriority: (uint16_t)priority_; +- (uint16_t) priority; +- (void) setWeight: (uint16_t)weight_; +- (uint16_t) weight; +- (void) setPort: (uint16_t)port_; +- (uint16_t) port; +- (void) setTarget: (OFString*)target_; +- (OFString*) target; +@end + +@interface XMPPSRVEnumerator: OFEnumerator +{ + OFString *domain; + OFList *priorityList; +} +#ifdef OF_HAVE_PROPERTIES +@property (copy) OFString *domain; +#endif + ++ enumeratorWithDomain: (OFString*)domain; + +- initWithDomain: (OFString*)domain; +- (void) setDomain: (OFString*)domain; +- (OFString*) domain; +- (void)XMPP_parseSRVRRWithHandle: (const ns_msg)handle + RR: (const ns_rr)rr + result: (XMPPSRVEntry*)result; +- (void)XMPP_addSRVEntry: (XMPPSRVEntry*)item + toSortedPriorityList: (OFList*)list; +@end ADDED src/XMPPSRVEnumerator.m Index: src/XMPPSRVEnumerator.m ================================================================== --- src/XMPPSRVEnumerator.m +++ src/XMPPSRVEnumerator.m @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2011, Florian Zeitz + * + * https://webkeks.org/hg/objxmpp/ + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice is present in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include + +#import "XMPPSRVEnumerator.h" + +@implementation XMPPSRVEntry +- (void)dealloc +{ + [target release]; + + [super dealloc]; +} + +- (OFString*)description +{ + return [OFString stringWithFormat: @"priority: %" PRIu16 + @" weight: %" PRIu16 + @" target: %@:%" PRIu16, priority, weight, + target, port]; +} + +- (void)setPriority: (uint16_t)priority_ +{ + priority = priority_; +} + +- (uint16_t)priority +{ + return priority; +} + +- (void)setWeight: (uint16_t)weight_ +{ + weight = weight_; +} + +- (uint16_t)weight +{ + return weight; +} + +- (void)setPort: (uint16_t)port_ +{ + port = port_; +} + +- (uint16_t)port +{ + return port; +} + +- (void)setTarget: (OFString*)target_ +{ + OFString *old = target; + target = [target_ copy]; + [old release]; +} + +- (OFString*)target +{ + return [[target copy] autorelease]; +} +@end + +@implementation XMPPSRVEnumerator ++ enumeratorWithDomain: (OFString*)domain_ +{ + return [[[self alloc] initWithDomain: domain_] autorelease]; +} + +- initWithDomain: (OFString*)domain_ +{ + self = [super init]; + priorityList = [[OFList alloc] init]; + [self setDomain: domain_]; + + return self; +} + +- (void)dealloc +{ + [priorityList release]; + [domain release]; + + [super dealloc]; +} + +- (void)setDomain: (OFString*)domain_ +{ + OFString *old = domain; + domain = [domain_ copy]; + [old release]; + [self reset]; +} + +- (OFString*)domain; +{ + return [[domain copy] autorelease]; +} + +- (void)XMPP_parseSRVRRWithHandle: (const ns_msg)handle + RR: (const ns_rr)rr + result: (XMPPSRVEntry*)result +{ + const uint16_t *rdata = (uint16_t*) ns_rr_rdata(rr); + char target[NS_MAXDNAME]; + [result setPriority: ntohs(rdata[0])]; + [result setWeight: ntohs(rdata[1])]; + [result setPort: ntohs(rdata[2])]; + dn_expand(ns_msg_base(handle), ns_msg_end(handle), + (uint8_t*) &rdata[3], target, NS_MAXDNAME); + [result setTarget: [OFString stringWithCString: target]]; +} + +- (id)nextObject +{ + if ([priorityList firstListObject]) { + uint16_t weight = 0; + of_list_object_t *iter; + XMPPSRVEntry *ret; + OFList *weightList = [priorityList firstObject]; + uint16_t maximumWeight = [[weightList lastObject] weight]; + + if (maximumWeight) { + RAND_pseudo_bytes((unsigned char *)&weight, 2); + weight %= maximumWeight; + } + + iter = [weightList firstListObject]; + while (iter) { + if (weight <= [iter->object weight]) { + ret = [iter->object retain]; + [weightList removeListObject: iter]; + if (![weightList firstListObject]) + [priorityList removeListObject: + [priorityList firstListObject]]; + return [ret autorelease]; + } + iter = iter->next; + } + assert(0); + } + + return nil; +} + +- (int)countByEnumeratingWithState: (of_fast_enumeration_state_t*)state + objects: (id*)objects + count: (int)count +{ + int len = 0; + XMPPSRVEntry *entry = [self nextObject]; + state->itemsPtr = objects; + while ((len < count) && entry) { + state->mutationsPtr = (unsigned long *)self; + objects[len++] = entry; + entry = [self nextObject]; + } + return len; +} + +- (void)reset +{ + int i, rrCount; + unsigned char *answer; + OFString *request; + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + + request = [OFString stringWithFormat: @"_xmpp-client._tcp.%@", domain]; + answer = [self allocMemoryWithSize: NS_MAXMSG]; + + res_ninit(&_res); + if (!(res_nsearch(&_res, [request cString], ns_c_in, ns_t_srv, answer, + NS_MAXMSG) < 0)) { + ns_rr rr; + ns_msg handle; + + ns_initparse(answer, NS_MAXMSG, &handle); + rrCount = ns_msg_count(handle, ns_s_an); + for (i = 0; i < rrCount ; i++) { + XMPPSRVEntry *result = [[XMPPSRVEntry alloc] init]; + ns_parserr(&handle, ns_s_an, i, &rr); + if ((ns_rr_type(rr) != ns_t_srv) + || ns_rr_class(rr) != ns_c_in) + @throw [OFInvalidServerReplyException + newWithClass: isa]; + + [self XMPP_parseSRVRRWithHandle: handle + RR: rr + result: result]; + + [self XMPP_addSRVEntry: result + toSortedPriorityList: priorityList]; + } + } + [self freeMemory: answer]; + [pool release]; + + +} + +- (void)XMPP_addSRVEntry: (XMPPSRVEntry*)item + toSortedPriorityList: (OFList*)list +{ + of_list_object_t *priorityIter = + [list firstListObject]; + while (1) { + if (priorityIter == NULL || + [[priorityIter->object firstObject] + priority] > [item priority]) { + OFList *newList = [OFList list]; + [newList appendObject: item]; + if (priorityIter) + [list insertObject: newList + beforeListObject: priorityIter]; + else + [list appendObject: newList]; + break; + } + if ([[priorityIter->object firstObject] priority] + == [item priority]) { + if ([item weight] == 0) + [priorityIter->object prependObject: item]; + else { + [item setWeight: [item weight] + + [[priorityIter->object lastObject] weight]]; + [priorityIter->object appendObject: item]; + } + break; + } + priorityIter = priorityIter->next; + } +} +@end Index: tests/test.m ================================================================== --- tests/test.m +++ tests/test.m @@ -100,12 +100,12 @@ [conn setServer: [arguments objectAtIndex: 0]]; [conn setUsername: [arguments objectAtIndex: 1]]; [conn setPassword: [arguments objectAtIndex: 2]]; [conn setResource: @"ObjXMPP"]; - [conn connect]; @try { + [conn connect]; [conn handleConnection]; } @catch (id e) { of_log(@"%@", e); } }