Deprecated: Assigning the return value of new by reference is deprecated in /home/bluestat/public_html/source/index.php on line 477
/*
* MacGDBp
* Copyright (c) 2007 - 2009, Blue Static
*
* This program 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 2 of the
* License, or (at your option) any later version.
*
* This program 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,
* write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#import "SocketWrapper.h"
#include
#include
#include
#include
#include
@interface SocketWrapper ()
@property (copy, readwrite, getter=remoteHost) NSString* hostname;
- (void)error:(NSString*)msg;
@end
@implementation SocketWrapper
@synthesize hostname;
/**
* Initializes the socket wrapper with a host and port
*/
- (id)initWithConnection:(GDBpConnection*)cnx
{
if (self = [super init])
{
connection = [cnx retain];
port = [connection port];
}
return self;
}
/**
* Dealloc
*/
- (void)dealloc
{
[connection release];
[hostname release];
[super dealloc];
}
/**
* Close our socket and clean up anything else
*/
- (void)close
{
close(sock);
}
/**
* Returns the delegate
*/
- (id)delegate
{
return delegate;
}
/**
* Sets the delegate but does* not* retain it
*/
- (void)setDelegate:(id)aDelegate
{
delegate = aDelegate;
}
/**
* Connects to a socket on the port specified during init. This will dispatch another thread to do the
* actual waiting. Delegate notifications are posted along the way to let the client know what is going on.
*/
- (void)connect
{
[NSThread detachNewThreadSelector:@selector(connect:) toTarget:self withObject:nil];
}
/**
* This does the actual dirty work (in a separate thread) of connecting to a socket
*/
- (void)connect:(id)obj
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
// create an INET socket that we'll be listen()ing on
int socketOpen = socket(PF_INET, SOCK_STREAM, 0);
// create our address given the port
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = htonl(INADDR_ANY);
memset(address.sin_zero, '\0', sizeof(address.sin_zero));
// allow an already-opened socket to be reused
int yes = 1;
setsockopt(socketOpen, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
// bind the socket... and don't give up until we've tried for a while
int tries = 0;
while (bind(socketOpen, (struct sockaddr*)&address, sizeof(address)) < 0)
{
if (tries >= 5)
{
close(socketOpen);
[self error:@"Could not bind to socket"];
[pool release];
return;
}
NSLog(@"couldn't bind to the socket... trying again in 5");
sleep(5);
tries++;
}
// now we just have to keep our ears open
if (listen(socketOpen, 0) == -1)
{
[self error:@"Could not use bound socket for listening"];
}
// accept a connection
struct sockaddr_in remoteAddress;
socklen_t remoteAddressLen = sizeof(remoteAddress);
sock = accept(socketOpen, (struct sockaddr*)&remoteAddress, &remoteAddressLen);
if (sock < 0)
{
close(socketOpen);
[self error:@"Client failed to accept remote socket"];
[pool release];
return;
}
// we're done listening now that we have a connection
close(socketOpen);
struct sockaddr_in addr;
socklen_t addrLength;
if (getpeername(sock, (struct sockaddr*)&addr, &addrLength) < 0)
{
[self error:@"Could not get remote hostname."];
}
char* name = inet_ntoa(addr.sin_addr);
[self setHostname:[NSString stringWithUTF8String:name]];
[connection performSelectorOnMainThread:@selector(socketDidAccept:) withObject:nil waitUntilDone:NO];
[pool release];
}
/**
* Reads from the socket and returns the result as a NSString (because it's always going to be XML). Be aware
* that the underlying socket recv() call will *wait* for the server to send a message, so be sure that this
* is used either in a threaded environment so the interface does not hang, or when you *know* the server
* will return something (which we almost always do). Returns the data that was received from the socket.
*/
- (NSString*)receive
{
// Read the first part of the response, the length of the packet.
char packetLength[8];
memset(&packetLength, 0x0, 8);
char c;
int i = 0;
while (recv(sock, &c, 1, 0) == 1 && c != 0x0)
packetLength[i++] = c;
int length = atoi(packetLength);
// Our final output.
NSMutableString* string = [[NSMutableString alloc] initWithCapacity:length];
// Create a buffer that we will move data from the network into.
char buffer[1024];
// The total amount of data we have currently read.
int received = 0;
// Loop until we have the entire packet.
while (received < length)
{
int size = recv(sock, &buffer, sizeof(buffer), 0);
if (size < 1)
{
[self error:@"Socket closed or could not be read"];
return nil;
}
NSString* temp = [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
[string appendString:temp];
received += [temp length];
}
return [string autorelease];
}
/**
* Sends a given NSString over the socket. Returns YES on complete submission.
*/
- (BOOL)send:(NSString*)data
{
data = [NSString stringWithFormat:@"%@\0", data];
int sent = send(sock, [data UTF8String], [data length], 0);
if (sent < 0)
{
[self error:@"Failed to write data to socket"];
return NO;
}
if (sent < [data length])
{
// TODO - do we really need to worry about partial sends with the lenght of our commands?
NSLog(@"FAIL: only partial packet was sent; sent %d bytes", sent);
return NO;
}
return YES;
}
/**
* Helper method that just calls -[DebuggerWindowController setError:] on the main thread
*/
- (void)error:(NSString*)msg
{
[delegate performSelectorOnMainThread:@selector(setError:) withObject:msg waitUntilDone:NO];
}
@end