2

I've dug around on a few questions on SO about AsyncSocket, but nothing's clicked. I feel what I'm trying to do is fairly simple, so it'll be face-palm... I have tried the non-GCD version, but no joy there either.

I need to be able to scan a range of hosts for a specific open TCP port. Unfortunately, I don't have control of the server code to have it announce itself using Bonjour. So, I'm down to brute force scanning and just attempting to connect on that one port -- if I get 'Connection refused', then I know to move on...

All this led me to AsyncSocket, which I think is the right tool for the job. Using the author's examples, I've got a basic working example, but not quite. Like his samples, I'm just using the main queue for simplicity. The thing is, it seems to be a crap shoot as to whether or not the delegate method get called. Sometimes the socketDidDisconnect: fires, sometimes not. In this particular case, I know that port 5002 is open on 192.168.1.7. When it gets to that element in the array, though, the didConnectToHost doesn't fire. However, if I remove everything but .7 from the list of IPs, then didConnectToHost does fire. I'm suspicious that calling connectToHost: in such a tight loop is an issue, but I can't prove it. If someone knows a simpler way to accomplish this, I'm open to it. Surprisingly, there's not a lot on this topic.

#import "GCDAsyncSocket.h"

#define PORT 5002

@implementation ViewController
{
    GCDAsyncSocket *asyncSocket;
    NSMutableArray *availableHosts;
}

- (IBAction)startScan
{
    NSArray *ipAddressList = @[@"192.168.1.1",@"192.168.1.2",@"192.168.1.3",@"192.168.1.4",@"192.168.1.5",@"192.168.1.6",@"192.168.1.7"];

    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:mainQueue];
    NSError *error = nil;

    for (int i = 1; i < ipAddressList.count; i++) {
        NSString *scanHostIP = ipAddressList[i];
        [asyncSocket connectToHost:scanHostIP onPort:PORT withTimeout:1 error:&error];
    }
}

- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
    NSLog(@"Found open port %d on %@", port, host);
    [availableBeds addObject:host];
    [sock setDelegate:nil];
    [sock disconnect];
    [sock setDelegate:self];

}

- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
    NSLog(@"Disconnected: %@", err ? err : @"");
}
Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
voodoobilly
  • 405
  • 7
  • 18

1 Answers1

1

I tried something similar, where I first found my own IP and then modified it to display the whole subnet range. I got something like "192.168.10." And then I do

- (void)startScan:(NSString *)ipRange
{
    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:mainQueue];
    NSError *error = nil;

    for (int i = 1; i < 255; i++) {
        NSString *scanHostIP = [NSString stringWithFormat:@"%@%d", ipRange, i];
        [asyncSocket connectToHost:scanHostIP onPort:PORT error:&error];
    }
    //connect to an IP that listens on the certain port
    [asyncSocket connectToHost:@"192.168.10.162" onPort:PORT error:&error]; 
}

When I try to connect to 162 in the loop the delegate method never gets called. But when I connect to only that specific IP it suddenly gets called. Interestingly enough the delegate method does't get called when I first run the loop and then try to connect to the specific host.

I tried to connect with and without timeout but neither does the trick.

I checked the connectToHost method and it always returns something. Even if I try to connect to 2 IPs it doesn't fire the delegate method.

To sum up my experience: The loop seems to somehow block the delegate method. I suspect that it's only meant to connect to one address? Then it would be the wrong tool for scanning a subnet.

EDIT: I found a nice solution. To scan the whole network I started used the following code:

-(void)startScan
{
    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:mainQueue];
    NSError *error = nil;

    NSString *scanHostIP = [NSString stringWithFormat:@"%@%d", ipRange, count];
    [asyncSocket connectToHost:scanHostIP onPort:PORT withTimeout:0.01 error:&error];
}

- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
    NSError *error;
    if(count < 254)
    {
        count++;
        NSString *scanHostIP = [NSString stringWithFormat:@"%@%d", ipRange, count];
        [asyncSocket connectToHost:scanHostIP onPort:PORT withTimeout:0.01 error:&error];
    }
}

The trick was to wait for the socket to time out and then start the new connection. This way you can scan the whole network in a tolerable time interval.

Hope this helps you or someone else.