summaryrefslogtreecommitdiffabout
path: root/pumpkin/TFTPPacket.m
Unidiff
Diffstat (limited to 'pumpkin/TFTPPacket.m') (more/less context) (ignore whitespace changes)
-rw-r--r--pumpkin/TFTPPacket.m203
1 files changed, 203 insertions, 0 deletions
diff --git a/pumpkin/TFTPPacket.m b/pumpkin/TFTPPacket.m
new file mode 100644
index 0000000..73f5995
--- a/dev/null
+++ b/pumpkin/TFTPPacket.m
@@ -0,0 +1,203 @@
1
2#import "TFTPPacket.h"
3
4@interface NSDictionary (TFTPOptions)
5
6- (size_t)tftpBytesLength;
7- (size_t)tftpGetBytes:(char*)p maxLength:(size_t)ml;
8
9@end
10@implementation NSDictionary (TFTPOptions)
11
12- (size_t)tftpBytesLength {
13 __block size_t rv = 0;
14 [self enumerateKeysAndObjectsUsingBlock:^(id k,id v,BOOL *s) {
15 rv += [k lengthOfBytesUsingEncoding:NSUTF8StringEncoding]+[v lengthOfBytesUsingEncoding:NSUTF8StringEncoding]+2;
16 }];
17 return rv;
18}
19
20- (size_t)tftpGetBytes:(char*)p maxLength:(size_t)ml {
21 __block char *_p = p;
22 __block size_t rl = ml;
23 __block size_t rv = 0;
24 [self enumerateKeysAndObjectsUsingBlock:^(NSString *k,NSString *v,BOOL *s) {
25 NSUInteger l;
26 [k getBytes:_p maxLength:rl usedLength:&l encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0,k.length) remainingRange:NULL];
27 _p+=l; *_p++=0; rl-=l+1; rv+=l+1;
28 [v getBytes:_p maxLength:rl usedLength:&l encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0,v.length) remainingRange:NULL];
29 _p+=l; *_p++=0; rl-=l+1; rv+=l+1;
30 }];
31 return rv;
32}
33
34@end
35
36@implementation TFTPPacket
37@synthesize data;
38
39-(BOOL) isRQOp {
40 return self.op==tftpOpRRQ || self.op==tftpOpWRQ;
41}
42-(BOOL) isOptionsOp {
43 return self.isRQOp || self.op==tftpOpOACK;
44}
45-(BOOL) isBlockOp {
46 return self.op==tftpOpDATA || self.op==tftpOpACK;
47}
48
49-(enum TFTPOp)op {
50 NSAssert(data.length,@"no data");
51 return (enum TFTPOp)ntohs(packet->op);
52}
53-(NSString*)rqFilename {
54 NSAssert( self.isRQOp, @"Wrong TFTP opcode for rq filename retrieval");
55 if(!memchr(packet->rq.data, 0, [data length]-sizeof(packet->op))) return nil;
56 return @(packet->rq.data);
57}
58-(NSString*)rqType {
59 NSAssert( self.isRQOp, @"Wrong TFTP opcode for rq type retrieval");
60 const char *z = (const char*)memchr(packet->rq.data,0, data.length-sizeof(packet->op));
61 if(!z) return nil;
62 if(!memchr(z+1,0,data.length-sizeof(packet->op)-(z-packet->rq.data))) return nil;
63 return @(z+1);
64}
65-(NSDictionary*)rqOptions {
66 enum TFTPOp op = self.op;
67 NSAssert( self.isOptionsOp, @"Wrong TFTP opcode for options retrieval");
68 const char *p = packet->any.data, *p1 = (const char*)packet + data.length;
69 if(op==tftpOpRRQ || op==tftpOpWRQ) {
70 p = (const char *)memchr(p,0,p1-p);
71 if(!p) return nil;
72 p = (const char *)memchr(p+1,0,p1-p);
73 if(!p) return nil;
74 ++p;
75 }
76 NSMutableDictionary *rv = [NSMutableDictionary dictionaryWithCapacity:8];
77 while(p<p1) {
78 const char *on = p;
79 p = (const char *)memchr(p,0,p1-p);
80 if(!p) break;
81 const char *ov = ++p;
82 p = (const char *)memchr(p,0,p1-p);
83 if(!p) break;
84 ++p;
85 rv[[@(on) lowercaseString]] = @(ov);
86 }
87 return rv;
88}
89-(uint16_t)block {
90 NSAssert( self.isBlockOp, @"Wrong TFTP opcode for block number retrieval");
91 return ntohs(*(uint16_t*)&packet->data);
92}
93-(NSData*)rqData {
94 NSAssert( self.op==tftpOpDATA, @"Can't get data from the request that doesn't have it");
95 return [NSData dataWithBytes:packet->data.data length:data.length-sizeof(packet->op)-sizeof(packet->data.block)];
96}
97-(uint16_t)rqCode {
98 NSAssert(self.op==tftpOpERROR,@"Wrong TFTP opcode for error code retrieval");
99 return ntohs(packet->err.code);
100}
101-(NSString*)rqMessage {
102 NSAssert(self.op==tftpOpERROR,@"Wrong TFTP opcode for error message retrieval");
103 return @(packet->err.data);
104}
105
106-(TFTPPacket*)initWithData:(NSData *)d {
107 if(!(self = [super init])) return self;
108 packet = (struct AnyTFTPPacket*)(data = [d retain]).bytes;
109 return self;
110}
111
112
113+(TFTPPacket*)packetWithData:(NSData*)d {
114 return [[[self alloc] initWithData:d] autorelease];
115}
116+(TFTPPacket*)packetWithBytesNoCopy:(void*)b andLength:(size_t)l {
117 return [[[self alloc] initWithData:[NSData dataWithBytesNoCopy:b length:l]] autorelease];
118}
119
120+(TFTPPacket*)packetErrorWithCode:(enum TFTPError)c andMessage:(NSString*)m {
121 NSUInteger ml = [m lengthOfBytesUsingEncoding:NSUTF8StringEncoding], bb;
122 struct AnyTFTPPacket *b = (struct AnyTFTPPacket*)malloc(bb = sizeof(b->op)+sizeof(b->err.code)+ml+1);
123 if(!b) return nil;
124 b->op = htons(tftpOpERROR);
125 b->err.code = ntohs(c);
126 [m getBytes:b->err.data maxLength:ml usedLength:NULL encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0,m.length) remainingRange:NULL];
127 b->err.data[ml]=0;
128 return [self packetWithBytesNoCopy:b andLength:bb];
129}
130+(TFTPPacket*)packetErrorWithErrno:(int)en andFallback:(NSString *)fb{
131 switch(en) {
132 case EACCES:
133 return [self packetErrorWithCode:tftpErrAccessViolation andMessage:@"acess violation"];
134 case ENOENT:
135 return [self packetErrorWithCode:tftpErrNotFound andMessage:@"not found"];
136 }
137 return [self packetErrorWithCode:tftpErrUndefined andMessage:fb];
138}
139
140+(TFTPPacket*)packetXRQWithOp:(enum TFTPOp)op file:(NSString*)f xferType:(NSString*)t andOptions:(NSDictionary*)o {
141 NSAssert(f && t && o,@"Something is amiss in packetXRQWithOp");
142 __block size_t dl = o.tftpBytesLength
143 +[f lengthOfBytesUsingEncoding:NSUTF8StringEncoding]
144 +[t lengthOfBytesUsingEncoding:NSUTF8StringEncoding]
145 +2;
146 size_t pl = dl;
147 struct AnyTFTPPacket *b = (struct AnyTFTPPacket*)malloc(pl+=sizeof(b->op));
148 if(!b) return nil;
149 b->op = htons(op);
150 __block char *p = b->rrq.data;
151 NSUInteger l;
152 [f getBytes:p maxLength:dl usedLength:&l encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0,f.length) remainingRange:NULL];
153 p+=l; *p++=0; dl-=l+1;
154 [t getBytes:p maxLength:dl usedLength:&l encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0,t.length) remainingRange:NULL];
155 p+=l; *p++=0; dl-=l+1;
156 l = [o tftpGetBytes:p maxLength:dl];
157 p+=l; dl-=l;
158 NSAssert1(dl==0,@"packet of the wrong size, remaining count: %lu",dl);
159 return [self packetWithBytesNoCopy:b andLength:pl];
160}
161
162+(TFTPPacket*)packetRRQWithFile:(NSString *)f xferType:(NSString *)t andOptions:(NSDictionary *)o {
163 return [self packetXRQWithOp:tftpOpRRQ file:f xferType:t andOptions:o];
164}
165+(TFTPPacket*)packetWRQWithFile:(NSString *)f xferType:(NSString *)t andOptions:(NSDictionary *)o {
166 return [self packetXRQWithOp:tftpOpWRQ file:f xferType:t andOptions:o];
167}
168
169+(TFTPPacket*)packetOACKWithOptions:(NSDictionary*)o {
170 __block NSUInteger pl = [o tftpBytesLength];
171 __block NSUInteger rc = pl;
172 __block struct AnyTFTPPacket *b = (struct AnyTFTPPacket*)malloc(pl+=sizeof(b->op));
173 if(!b) return nil;
174 b->op = htons(tftpOpOACK);
175 __block char *p = b->oack.data;
176 rc -= [o tftpGetBytes:p maxLength:pl];
177 NSAssert1(rc==0,@"packet of the wrong size, remaining count: %lu",rc);
178 return [self packetWithBytesNoCopy:b andLength:pl];
179}
180+(TFTPPacket*)packetDataWithBlock:(uint16_t)b andData:(NSData*)d {
181 NSUInteger pl;
182 struct AnyTFTPPacket *p = (struct AnyTFTPPacket*)malloc(pl=sizeof(p->op)+sizeof(p->data.block)+d.length);
183 if(!p) return nil;
184 p->op = htons(tftpOpDATA);
185 p->data.block = htons(b);
186 [d getBytes:p->data.data length:d.length];
187 return [self packetWithBytesNoCopy:p andLength:pl];
188}
189+(TFTPPacket*)packetACKWithBlock:(uint16_t)b {
190 NSUInteger pl;
191 struct AnyTFTPPacket *p = (struct AnyTFTPPacket*)malloc(pl=sizeof(p->op)+sizeof(p->ack.block));
192 if(!p) return nil;
193 p->op = htons(tftpOpACK);
194 p->ack.block = htons(b);
195 return [self packetWithBytesNoCopy:p andLength:pl];
196}
197
198-(void)dealloc {
199 [data release];
200 [super dealloc];
201}
202
203@end