summaryrefslogtreecommitdiffabout
path: root/pumpkin/DaemonListener.m
Side-by-side diff
Diffstat (limited to 'pumpkin/DaemonListener.m') (more/less context) (ignore whitespace changes)
-rw-r--r--pumpkin/DaemonListener.m111
1 files changed, 111 insertions, 0 deletions
diff --git a/pumpkin/DaemonListener.m b/pumpkin/DaemonListener.m
new file mode 100644
index 0000000..ab14296
--- a/dev/null
+++ b/pumpkin/DaemonListener.m
@@ -0,0 +1,111 @@
+#import "DaemonListener.h"
+#import "TFTPPacket.h"
+#import "SendXFer.h"
+#import "ReceiveXFer.h"
+#import "StringsAttached.h"
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/stat.h>
+
+static void cbListener(CFSocketRef sockie,CFSocketCallBackType cbt,CFDataRef cba,
+ const void *cbd,void *i) {
+ [(DaemonListener*)i callbackWithType:cbt addr:cba data:cbd];
+}
+
+@implementation DaemonListener
+
+-(void)callbackWithType:(CFSocketCallBackType)t addr:(CFDataRef)a data:(const void *)d {
+ switch(t) {
+ case kCFSocketDataCallBack:
+ {
+ struct sockaddr_in *sin = (struct sockaddr_in*)CFDataGetBytePtr(a);
+ if([pumpkin hasPeer:sin]) {
+ [pumpkin log:@"I'm already processing the request from %@",[NSString stringWithSocketAddress:sin]];
+ return;
+ }
+ TFTPPacket *p = [TFTPPacket packetWithData:(NSData*)d];
+ switch([p op]) {
+ case tftpOpRRQ: [[[SendXFer alloc] initWithPeer:sin andPacket:p] autorelease]; break;
+ case tftpOpWRQ: [[[ReceiveXFer alloc] initWithPeer:sin andPacket:p] autorelease]; break;
+ default:
+ [pumpkin log:@"Invalid OP %d received from %@",p.op,[NSString stringWithSocketAddress:sin]];
+ break;
+ }
+ }
+ break;
+ default:
+ NSLog(@"unhandled callback: %lu",t);
+ break;
+ }
+}
+
+
+-(DaemonListener*)initWithAddress:(struct sockaddr_in*)sin {
+ if(!(self=[super init])) return self;
+
+ pumpkin = NSApplication.sharedApplication.delegate;
+
+ @try {
+ CFSocketContext ctx;
+ ctx.version = 0;
+ ctx.info = self;
+ ctx.retain = 0; ctx.release = 0;
+ ctx.copyDescription = 0;
+ sockie = CFSocketCreate(kCFAllocatorDefault,PF_INET,SOCK_DGRAM,IPPROTO_UDP,
+ kCFSocketReadCallBack|kCFSocketDataCallBack,
+ cbListener,&ctx);
+ if(ntohs(sin->sin_port)>1024) {
+ NSData *nsd = [NSData dataWithBytes:sin length:sizeof(*sin)];
+ if(CFSocketSetAddress(sockie, (CFDataRef)nsd))
+ [[NSException exceptionWithName:@"BindFailure"
+ reason:[NSString stringWithFormat:@"Binding failed, error code: %d", errno]
+ userInfo:@{@"errno": @errno}
+ ] raise];
+ }else{
+ const char *args[] = {
+ 0,
+ [[NSString stringWithFormat:@"%d", CFSocketGetNative(sockie)] UTF8String],
+ [[NSString stringWithHostAddress:sin] UTF8String],
+ [[NSString stringWithPortNumber:sin] UTF8String],
+ NULL
+ };
+ [pumpkin runBiportal:args];
+ }
+ }@catch(NSException *e) {
+ if(sockie) {
+ CFSocketInvalidate(sockie);
+ CFRelease(sockie);
+ }
+ @throw;
+ }
+
+ runloopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sockie, 0);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(),runloopSource, kCFRunLoopDefaultMode);
+ return self;
+}
+
+-(void)dealloc {
+ if(runloopSource) {
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runloopSource, kCFRunLoopDefaultMode);
+ CFRelease(runloopSource);
+ }
+ if(sockie) {
+ CFSocketInvalidate(sockie);
+ CFRelease(sockie);
+ }
+ [super dealloc];
+}
+
++(DaemonListener*) listenerWithDefaults {
+ struct sockaddr_in sin;
+ memset(&sin,0,sizeof(sin));
+ sin.sin_len=sizeof(sin);
+ sin.sin_family=AF_INET;
+ id d = [[NSUserDefaultsController sharedUserDefaultsController] values];
+ sin.sin_port=htons([[d valueForKey:@"bindPort"] intValue]);
+ sin.sin_addr.s_addr=inet_addr([[d valueForKey:@"bindAddress"] UTF8String]);
+ return [[[DaemonListener alloc] initWithAddress:&sin] autorelease];
+}
+
+@end