Sockets don't quite work, but they will almost work.
[ITFoundation.git] / ITInetSocket.m
1 //
2 //  ITInetSocket.m
3 //  ITFoundation
4 //
5 //  Created by Alexander Strange on Tue Feb 11 2003.
6 //  Copyright (c) 2003 __MyCompanyName__. All rights reserved.
7 //
8
9 #import "ITInetSocket.h"
10 #import "ITServiceBrowserDelegate.h"
11 #import <sys/socket.h>
12 #import <arpa/inet.h>
13 #import <unistd.h>
14
15 @interface ITInetSocket(Debugging)
16 -(NSString*)dumpv6Addrinfo:(struct addrinfo *)_ai;
17 @end
18
19 @interface ITInetSocket(Private)
20 -(void)doConnSetupWithHost:(NSString*)host namedPort:(NSString*)namedPort;
21 -(void)realDoConnection;
22 -(void)spinoffReadLoop;
23 -(void)socketReadLoop:(id)data;
24 @end
25
26 @implementation ITInetSocket
27 +(void)startAutoconnectingToService:(NSString*)type delegate:(id <ITInetSocketDelegate,NSObject>)d
28 {
29     NSNetServiceBrowser *browse = [[NSNetServiceBrowser alloc] init];
30     ITServiceBrowserDelegate *bd = [[ITServiceBrowserDelegate alloc] initWithDelegate:d];
31
32     [browse setDelegate:bd];
33     [browse searchForServicesOfType:[NSString stringWithFormat:@"_%@._tcp.",type] inDomain:@""];
34 }
35
36 -(id)initWithFD:(int)fd delegate:(id <ITInetSocketDelegate,NSObject>)d
37 {
38     if (self = [super init])
39            {
40            state = ITInetSocketListening;
41            sockfd = fd;
42            delegate = [d retain];
43            port = 0;
44            writePipe = [[ITByteStream alloc] initWithDelegate:self];
45            readPipe = [[ITByteStream alloc] initWithDelegate:d];
46            ai = nil;
47            sarr = nil;
48            bufs = 512;
49            actionflag = dieflag = 0;
50            nc = 0;
51            }
52     [self spinoffReadLoop];
53     return self;
54 }
55
56 -(id)initWithDelegate:(id <ITInetSocketDelegate,NSObject>)d
57 {
58     if (self = [super init])
59            {
60            state = ITInetSocketDisconnected;
61            sockfd = -1;
62            delegate = [d retain];
63            port = 0;
64            writePipe = [[ITByteStream alloc] initWithDelegate:self];
65            readPipe = [[ITByteStream alloc] initWithDelegate:d];
66            ai = nil;
67            sarr = nil;
68            bufs = 512;
69            actionflag = dieflag = 0;
70            nc = 1;
71            }
72     return self;
73 }
74
75
76 -(void) dealloc
77 {
78     shutdown(sockfd,2);
79     [delegate release];
80     [writePipe release];
81     [readPipe release];
82     if (!sarr) freeaddrinfo(ai);
83     [sarr release];
84 }
85
86 -(void) connectToHost:(NSString*)host onPort:(short)thePort
87 {
88     if (state == ITInetSocketDisconnected)
89            {
90            NSString *nport = [[NSNumber numberWithShort:thePort] stringValue];
91            [self doConnSetupWithHost:host namedPort:nport];
92            }
93 }
94
95 -(void) connectToHost:(NSString*)host onNamedPort:(NSString*)_port
96 {
97     if (state == ITInetSocketDisconnected)
98            {
99            [self doConnSetupWithHost:host namedPort:_port];
100            }
101 }
102
103 -(void) connectWithSockaddrArray:(NSArray*)arr
104 {
105     if (state == ITInetSocketDisconnected)
106            {
107            NSEnumerator *e = [arr objectEnumerator];
108            NSData *d;
109            struct addrinfo *a;
110            ai = malloc(sizeof(struct addrinfo));
111            a = ai;
112            while (d = [e nextObject])
113            {
114                   struct sockaddr *s = (struct sockaddr*)[d bytes];
115                   bzero(a,sizeof(struct addrinfo));
116                   a->ai_family = s->sa_family;
117                   a->ai_addr = s;
118                   a->ai_next = malloc(sizeof(struct addrinfo));
119                   a = a->ai_next;
120            }
121            ai_cur = ai;
122            [self realDoConnection];
123            }
124 }
125
126 -(void)disconnect
127 {
128     NSLog(@"Got a disconnect");
129     dieflag = 1;
130 }
131
132 -(void)retryConnection
133 {
134     ai_cur = ai_cur->ai_next?ai_cur->ai_next:ai_cur;
135     [self disconnect];
136     [self realDoConnection];
137 }
138
139 -(ITInetSocketState)state
140 {
141     return state;
142 }
143 -(id <ITInetSocketDelegate>)delegate
144 {
145     return delegate;
146 }
147
148 -(unsigned short)bufferSize
149 {
150     return bufs;
151 }
152
153 -(void)setBufferSize:(unsigned short)_bufs
154 {
155     bufs = _bufs;
156 }
157
158 -(void)newDataAdded:(ITByteStream*)sender
159 {
160     NSLog(@"writePipe got something");
161     actionflag = 1;
162     do {} while (actionflag == 1);
163     NSLog(@"thread saw actionFlag");
164 }
165 @end
166
167 @implementation ITInetSocket(Debugging)
168 -(NSString*)dumpv6Addrinfo:(struct addrinfo *)_ai
169 {
170     const char *cfmt =
171     "{\n"
172     "Flags = %x\n"
173     "Family = %x\n"
174     "Socktype = %x\n"
175     "Protocol = %x\n"
176     "Canonname = %s\n"
177     "Sockaddr = \n"
178     "\t{\n"
179     "\tLength = %x\n"
180     "\tFamily = %x\n"
181     "\tPort = %d\n"
182     "\tFlowinfo = %x\n"
183     "\tAddr = {%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx}\n"
184     "\tScope = %x\n"
185     "\t}\n"
186     "Next = %@\n"
187     "}\n";
188     NSString *nsfmt = [NSString stringWithCString:cfmt];
189     struct sockaddr_in6 *sa = (struct sockaddr_in6 *)_ai->ai_addr;
190     NSString *buf = [[NSMutableString alloc] initWithFormat:nsfmt,_ai->ai_flags,_ai->ai_family,_ai->ai_socktype,_ai->ai_protocol,_ai->ai_canonname?_ai->ai_canonname:"",sa->sin6_len,sa->sin6_family,sa->sin6_port,sa->sin6_flowinfo,sa->sin6_addr.__u6_addr.__u6_addr16[0],sa->sin6_addr.__u6_addr.__u6_addr16[1],sa->sin6_addr.__u6_addr.__u6_addr16[2],sa->sin6_addr.__u6_addr.__u6_addr16[3],sa->sin6_addr.__u6_addr.__u6_addr16[4],sa->sin6_addr.__u6_addr.__u6_addr16[5],sa->sin6_addr.__u6_addr.__u6_addr16[6],sa->sin6_addr.__u6_addr.__u6_addr16[7],sa->sin6_scope_id,_ai->ai_next?[self dumpv6Addrinfo:_ai->ai_next]:@"nil"];
191
192     return buf;
193 }
194 @end
195
196 @implementation ITInetSocket(Private)
197 -(void)doConnSetupWithHost:(NSString*)host namedPort:(NSString*)namedPort
198 {
199     struct addrinfo hints;
200            int err;
201            const char *portNam = [namedPort cString], *hostCStr = [host cString];
202            state = ITInetSocketConnecting;
203            hints.ai_flags = 0;
204            hints.ai_family = PF_UNSPEC;
205            hints.ai_socktype = SOCK_STREAM;
206            hints.ai_protocol = IPPROTO_TCP;
207            hints.ai_addrlen = 0;
208            hints.ai_canonname = NULL;
209            hints.ai_addr = NULL;
210            hints.ai_next = NULL;
211
212            err = getaddrinfo(hostCStr,portNam,&hints,&ai);
213
214            NSLog(@"%s, h %@ p %@",gai_strerror(err),host,namedPort);
215            NSLog(ai?[self dumpv6Addrinfo:ai]:@"");
216            ai_cur = ai;
217            [self realDoConnection];
218 }
219
220 -(void)realDoConnection
221 {
222     sockfd = socket(ai_cur->ai_family,SOCK_STREAM,IPPROTO_TCP);
223     [self spinoffReadLoop];
224 }
225
226 -(void)spinoffReadLoop
227 {
228     NSPort *p1 = [NSPort port], *p2 = [NSPort port];
229     NSConnection *dcon = [[NSConnection alloc] initWithReceivePort:p1 sendPort:p2];
230     NSArray *par = [NSArray arrayWithObjects:p2,p1,nil];
231     [dcon setRootObject:delegate];
232     [NSThread detachNewThreadSelector:@selector(socketReadLoop:) toTarget:self withObject:par]; //spawn read thread
233 }
234
235 -(void)socketReadLoop:(id)data
236 {
237     NSAutoreleasePool *ap = [[NSAutoreleasePool alloc] init];
238     NSConnection *dcon = [[NSConnection alloc] initWithReceivePort:[data objectAtIndex:0] sendPort:[data objectAtIndex:1]];
239     NSProxy *dp = [[dcon rootProxy] retain];
240     char *buf = malloc(bufs);
241     unsigned long readLen = 0;
242     signed int err;
243     [readPipe setDelegate:dp];
244     if (nc){
245     NSLog(@"Connecting");
246     err = connect(sockfd,ai_cur->ai_addr,ai_cur->ai_addrlen);
247     if (err == -1)
248            {
249            perror("CAwh");
250            [(id)dp errorOccured:ITInetCouldNotConnect during:ITInetSocketConnecting onSocket:self];
251            goto dieaction;
252            }
253     }
254     NSLog(@"Sending finishedConnecting");
255     [(id)dp finishedConnecting:self];
256 lstart:
257            state = ITInetSocketListening;
258            while (!actionflag && !dieflag)
259            {
260                   readLen = recv(sockfd,buf,bufs,0);
261                   state = ITInetSocketReading;
262                   if (readLen == -1) {[(id)dp errorOccured:ITInetConnectionDropped during:ITInetSocketReading onSocket:self];goto dieaction;}
263                   if (readLen) {
264                          NSLog(@"recv'd");
265                          NSLog(@"Doing writeData to readPipe");
266                          [readPipe writeBytes:buf len:readLen];
267                          [ap release];
268                          ap = [[NSAutoreleasePool alloc] init];
269                   }
270            }
271            state = ITInetSocketListening;
272     actionflag = 0;
273
274     if (dieflag)
275            {
276 dieaction:
277            state = ITInetSocketDisconnected;
278            perror("Awh");
279            free(buf);
280            shutdown(sockfd,2);
281            [dcon release];
282            [dp release];
283            [ap release];
284            dieflag = 0;
285            return;
286            }
287
288     {
289            state = ITInetSocketWriting;
290            NSLog(@"Emptying writePipe");
291            NSData *d = [writePipe readAllData];
292            write(sockfd,[d bytes],[d length]);
293            [ap release];
294            ap = [[NSAutoreleasePool alloc] init];
295            goto lstart;
296            }
297     goto dieaction;
298 }
299 @end