Skip to content

Commit 685cb8f

Browse files
committed
Validating Info/Query (IQ) stanzas in the Extensible Messaging and Presence Protocol (XMPP)
Fixes robbiehanson#300
1 parent b837b69 commit 685cb8f

File tree

6 files changed

+234
-41
lines changed

6 files changed

+234
-41
lines changed

Core/XMPPStream.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
@class XMPPMessage;
1515
@class XMPPPresence;
1616
@class XMPPModule;
17+
@class XMPPElement;
1718
@class XMPPElementReceipt;
1819
@protocol XMPPStreamDelegate;
1920

@@ -589,6 +590,18 @@ extern const NSTimeInterval XMPPStreamTimeoutNone;
589590
**/
590591
- (void)resendMyPresence;
591592

593+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
594+
#pragma mark Stanza Validation
595+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
596+
597+
/**
598+
* Validates that a response element is FROM the jid that the request element was sent TO.
599+
* Supports validating responses when request didn't specify a TO.
600+
**/
601+
- (BOOL)isValidResponseElementFrom:(XMPPJID *)from forRequestElementTo:(XMPPJID *)to;
602+
603+
- (BOOL)isValidResponseElement:(XMPPElement *)response forRequestElement:(XMPPElement *)request;
604+
592605
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
593606
#pragma mark Module Plug-In System
594607
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Core/XMPPStream.m

Lines changed: 99 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#import "XMPPParser.h"
33
#import "XMPPLogging.h"
44
#import "XMPPInternal.h"
5+
#import "XMPPIDTracker.h"
56
#import "XMPPSRVResolver.h"
67
#import "NSData+XMPP.h"
78

@@ -130,6 +131,8 @@ @interface XMPPStream ()
130131
XMPPSRVResolver *srvResolver;
131132
NSArray *srvResults;
132133
NSUInteger srvResultsIndex;
134+
135+
XMPPIDTracker *idTracker;
133136

134137
NSMutableArray *receipts;
135138

@@ -208,6 +211,8 @@ - (void)commonInit
208211

209212
registeredModules = [[NSMutableArray alloc] init];
210213
autoDelegateDict = [[NSMutableDictionary alloc] init];
214+
215+
idTracker = [[XMPPIDTracker alloc] initWithStream:self dispatchQueue:xmppQueue];
211216

212217
receipts = [[NSMutableArray alloc] init];
213218
}
@@ -278,6 +283,8 @@ - (void)dealloc
278283
dispatch_source_cancel(keepAliveTimer);
279284
}
280285

286+
[idTracker removeAllIDs];
287+
281288
for (XMPPElementReceipt *receipt in receipts)
282289
{
283290
[receipt signalFailure];
@@ -1620,11 +1627,10 @@ - (BOOL)registerWithElements:(NSArray *)elements error:(NSError **)errPtr
16201627
[queryElement addChild:element];
16211628
}
16221629

1623-
NSXMLElement *iqElement = [NSXMLElement elementWithName:@"iq"];
1624-
[iqElement addAttributeWithName:@"type" stringValue:@"set"];
1625-
[iqElement addChild:queryElement];
1630+
XMPPIQ *iq = [XMPPIQ iqWithType:@"set"];
1631+
[iq addChild:queryElement];
16261632

1627-
NSString *outgoingStr = [iqElement compactXMLString];
1633+
NSString *outgoingStr = [iq compactXMLString];
16281634
NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
16291635

16301636
XMPPLogSend(@"SEND: %@", outgoingStr);
@@ -3274,9 +3280,7 @@ - (void)handleStreamFeatures
32743280
NSXMLElement *bind = [NSXMLElement elementWithName:@"bind" xmlns:@"urn:ietf:params:xml:ns:xmpp-bind"];
32753281
[bind addChild:resource];
32763282

3277-
NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"];
3278-
[iq addAttributeWithName:@"type" stringValue:@"set"];
3279-
[iq addAttributeWithName:@"id" stringValue:[self generateUUID]];
3283+
XMPPIQ *iq = [XMPPIQ iqWithType:@"set" elementID:[self generateUUID]];
32803284
[iq addChild:bind];
32813285

32823286
NSString *outgoingStr = [iq compactXMLString];
@@ -3288,16 +3292,19 @@ - (void)handleStreamFeatures
32883292
[asyncSocket writeData:outgoingData
32893293
withTimeout:TIMEOUT_XMPP_WRITE
32903294
tag:TAG_XMPP_WRITE_STREAM];
3295+
3296+
[idTracker addElement:iq
3297+
target:nil
3298+
selector:NULL
3299+
timeout:XMPPIDTrackerTimeoutNone];
32913300
}
32923301
else
32933302
{
32943303
// The user didn't specify a resource, so we ask the server to bind one for us
32953304

32963305
NSXMLElement *bind = [NSXMLElement elementWithName:@"bind" xmlns:@"urn:ietf:params:xml:ns:xmpp-bind"];
32973306

3298-
NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"];
3299-
[iq addAttributeWithName:@"type" stringValue:@"set"];
3300-
[iq addAttributeWithName:@"id" stringValue:[self generateUUID]];
3307+
XMPPIQ *iq = [XMPPIQ iqWithType:@"set" elementID:[self generateUUID]];
33013308
[iq addChild:bind];
33023309

33033310
NSString *outgoingStr = [iq compactXMLString];
@@ -3309,6 +3316,11 @@ - (void)handleStreamFeatures
33093316
[asyncSocket writeData:outgoingData
33103317
withTimeout:TIMEOUT_XMPP_WRITE
33113318
tag:TAG_XMPP_WRITE_STREAM];
3319+
3320+
[idTracker addElement:iq
3321+
target:nil
3322+
selector:NULL
3323+
timeout:XMPPIDTrackerTimeoutNone];
33123324
}
33133325

33143326
// We're already listening for the response...
@@ -3481,9 +3493,7 @@ - (void)handleBinding:(NSXMLElement *)response
34813493
NSXMLElement *session = [NSXMLElement elementWithName:@"session"];
34823494
[session setXmlns:@"urn:ietf:params:xml:ns:xmpp-session"];
34833495

3484-
NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"];
3485-
[iq addAttributeWithName:@"type" stringValue:@"set"];
3486-
[iq addAttributeWithName:@"id" stringValue:[self generateUUID]];
3496+
XMPPIQ *iq = [XMPPIQ iqWithType:@"set" elementID:[self generateUUID]];
34873497
[iq addChild:session];
34883498

34893499
NSString *outgoingStr = [iq compactXMLString];
@@ -3495,6 +3505,11 @@ - (void)handleBinding:(NSXMLElement *)response
34953505
[asyncSocket writeData:outgoingData
34963506
withTimeout:TIMEOUT_XMPP_WRITE
34973507
tag:TAG_XMPP_WRITE_STREAM];
3508+
3509+
[idTracker addElement:iq
3510+
target:nil
3511+
selector:NULL
3512+
timeout:XMPPIDTrackerTimeoutNone];
34983513

34993514
// Update state
35003515
state = STATE_XMPP_START_SESSION;
@@ -3601,6 +3616,11 @@ - (void)continueHandleBinding:(NSString *)alternativeResource
36013616
[asyncSocket writeData:outgoingData
36023617
withTimeout:TIMEOUT_XMPP_WRITE
36033618
tag:TAG_XMPP_WRITE_STREAM];
3619+
3620+
[idTracker addElement:iq
3621+
target:nil
3622+
selector:NULL
3623+
timeout:XMPPIDTrackerTimeoutNone];
36043624

36053625
// The state remains in STATE_XMPP_BINDING
36063626
}
@@ -3622,6 +3642,11 @@ - (void)continueHandleBinding:(NSString *)alternativeResource
36223642
[asyncSocket writeData:outgoingData
36233643
withTimeout:TIMEOUT_XMPP_WRITE
36243644
tag:TAG_XMPP_WRITE_STREAM];
3645+
3646+
[idTracker addElement:iq
3647+
target:nil
3648+
selector:NULL
3649+
timeout:XMPPIDTrackerTimeoutNone];
36253650

36263651
// The state remains in STATE_XMPP_BINDING
36273652
}
@@ -3898,6 +3923,9 @@ - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
38983923
// Clear srv results
38993924
srvResolver = nil;
39003925
srvResults = nil;
3926+
3927+
// Stop tracking IDs
3928+
[idTracker removeAllIDs];
39013929

39023930
// Clear any pending receipts
39033931
for (XMPPElementReceipt *receipt in receipts)
@@ -4010,8 +4038,7 @@ - (void)xmppParser:(XMPPParser *)sender didReadRoot:(NSXMLElement *)root
40104038

40114039
NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:auth"];
40124040

4013-
NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"];
4014-
[iq addAttributeWithName:@"type" stringValue:@"get"];
4041+
XMPPIQ *iq = [XMPPIQ iqWithType:@"get" elementID:[self generateUUID]];
40154042
[iq addChild:query];
40164043

40174044
NSString *outgoingStr = [iq compactXMLString];
@@ -4073,13 +4100,23 @@ - (void)xmppParser:(XMPPParser *)sender didReadElement:(NSXMLElement *)element
40734100
}
40744101
else if (state == STATE_XMPP_BINDING)
40754102
{
4076-
// The response from our binding request
4077-
[self handleBinding:element];
4103+
XMPPIQ *iq = [XMPPIQ iqFromElement:element];
4104+
4105+
if([idTracker invokeForElement:iq withObject:element])
4106+
{
4107+
// The response from our binding request
4108+
[self handleBinding:element];
4109+
}
40784110
}
40794111
else if (state == STATE_XMPP_START_SESSION)
40804112
{
4081-
// The response from our start session request
4082-
[self handleStartSessionResponse:element];
4113+
XMPPIQ *iq = [XMPPIQ iqFromElement:element];
4114+
4115+
if([idTracker invokeForElement:iq withObject:element])
4116+
{
4117+
// The response from our start session request
4118+
[self handleStartSessionResponse:element];
4119+
}
40834120
}
40844121
else
40854122
{
@@ -4230,6 +4267,49 @@ - (void)keepAlive
42304267
}
42314268
}
42324269

4270+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
4271+
#pragma mark Stanza Validation
4272+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
4273+
4274+
- (BOOL)isValidResponseElementFrom:(XMPPJID *)from forRequestElementTo:(XMPPJID *)to
4275+
{
4276+
BOOL valid = YES;
4277+
4278+
if(to)
4279+
{
4280+
if(![to isEqualToJID:from])
4281+
{
4282+
valid = NO;
4283+
}
4284+
}
4285+
/**
4286+
* Replies for Stanza's that had no TO will be accepted if the FROM is:
4287+
*
4288+
* No from.
4289+
* from = the bare account JID.
4290+
* from = the full account JID (legal in 3920, but not 6120).
4291+
* from = the server's domain.
4292+
**/
4293+
else if(!to && from)
4294+
{
4295+
if(![from isEqualToJID:self.myJID options:XMPPJIDCompareBare]
4296+
&& ![from isEqualToJID:self.myJID options:XMPPJIDCompareFull]
4297+
&& ![from isEqualToJID:[self.myJID domainJID] options:XMPPJIDCompareFull])
4298+
{
4299+
valid = NO;
4300+
}
4301+
}
4302+
4303+
return valid;
4304+
}
4305+
4306+
- (BOOL)isValidResponseElement:(XMPPElement *)response forRequestElement:(XMPPElement *)request
4307+
{
4308+
if(!response || !request) return NO;
4309+
4310+
return [self isValidResponseElementFrom:[response from] forRequestElementTo:[request to]];
4311+
}
4312+
42334313
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
42344314
#pragma mark Module Plug-In System
42354315
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Extensions/Roster/XMPPRoster.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ - (BOOL)activate:(XMPPStream *)aXmppStream
9191
{
9292
XMPPLogVerbose(@"%@: Activated", THIS_FILE);
9393

94-
xmppIDTracker = [[XMPPIDTracker alloc] initWithDispatchQueue:moduleQueue];
94+
xmppIDTracker = [[XMPPIDTracker alloc] initWithStream:xmppStream dispatchQueue:moduleQueue];
9595

9696
#ifdef _XMPP_VCARD_AVATAR_MODULE_H
9797
{
@@ -798,7 +798,7 @@ - (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq
798798
}
799799
else if([iq isResultIQ])
800800
{
801-
[xmppIDTracker invokeForID:[iq elementID] withObject:iq];
801+
[xmppIDTracker invokeForElement:iq withObject:iq];
802802
}
803803

804804
return YES;

Extensions/XEP-0054/XMPPvCardTempModule.m

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ - (BOOL)activate:(XMPPStream *)aXmppStream
8484
{
8585
// Custom code goes here (if needed)
8686

87-
_myvCardTracker = [[XMPPIDTracker alloc] initWithDispatchQueue:moduleQueue];
87+
_myvCardTracker = [[XMPPIDTracker alloc] initWithStream:xmppStream dispatchQueue:moduleQueue];
8888

8989
return YES;
9090
}
@@ -185,16 +185,14 @@ - (void)updateMyvCardTemp:(XMPPvCardTemp *)vCardTemp
185185
dispatch_block_t block = ^{ @autoreleasepool {
186186

187187
XMPPvCardTemp *newvCardTemp = [vCardTemp copy];
188-
189-
NSString *myvCardElementID = [xmppStream generateUUID];
190188

191-
XMPPIQ *iq = [XMPPIQ iqWithType:@"set" to:nil elementID:myvCardElementID child:newvCardTemp];
189+
XMPPIQ *iq = [XMPPIQ iqWithType:@"set" to:nil elementID:[xmppStream generateUUID] child:newvCardTemp];
192190
[xmppStream sendElement:iq];
193191

194-
[_myvCardTracker addID:myvCardElementID
195-
target:self
196-
selector:@selector(handleMyvcard:withInfo:)
197-
timeout:600];
192+
[_myvCardTracker addElement:iq
193+
target:self
194+
selector:@selector(handleMyvcard:withInfo:)
195+
timeout:600];
198196

199197
[self _updatevCardTemp:newvCardTemp forJID:[xmppStream myJID]];
200198

@@ -261,7 +259,7 @@ - (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq
261259
{
262260
// This method is invoked on the moduleQueue.
263261

264-
[_myvCardTracker invokeForID:[iq elementID] withObject:iq];
262+
[_myvCardTracker invokeForElement:iq withObject:iq];
265263

266264
// Remember XML heirarchy memory management rules.
267265
// The passed parameter is a subnode of the IQ, and we need to pass it to an asynchronous operation.

0 commit comments

Comments
 (0)