summaryrefslogtreecommitdiffabout
path: root/pumpkin/DaemonListener.m
blob: ab142960ef76f665fdbddf30383735d3d3f4f0e0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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