Doesn't crash now. Of course, there's still lots of problems.
[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:nil];
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            }
51     return self;
52 }
53
54 -(id)initWithDelegate:(id <ITInetSocketDelegate,NSObject>)d
55 {
56     if (self = [super init])
57            {
58            state = ITInetSocketDisconnected;
59            sockfd = -1;
60            delegate = [d retain];
61            port = 0;
62            writePipe = [[ITByteStream alloc] initWithDelegate:self];
63            readPipe = [[ITByteStream alloc] initWithDelegate:d];
64            ai = nil;
65            sarr = nil;
66            bufs = 512;
67            actionflag = dieflag = 0;
68            }
69     return self;
70 }
71
72
73 -(void) dealloc
74 {
75     shutdown(sockfd,2);
76     [delegate release];
77     [writePipe release];
78     [readPipe release];
79     if (!sarr) freeaddrinfo(ai);
80     [sarr release];
81 }
82
83 -(void) connectToHost:(NSString*)host onPort:(short)thePort
84 {
85     if (state == ITInetSocketDisconnected)
86            {
87            NSString *nport = [[NSNumber numberWithShort:thePort] stringValue];
88            [self doConnSetupWithHost:host namedPort:nport];
89            }
90 }
91
92 -(void) connectToHost:(NSString*)host onNamedPort:(NSString*)_port
93 {
94     if (state == ITInetSocketDisconnected)
95            {
96            [self doConnSetupWithHost:host namedPort:_port];
97            }
98 }
99
100 -(void) connectWithSockaddrArray:(NSArray*)arr
101 {
102     if (state == ITInetSocketDisconnected)
103            {
104            NSEnumerator *e = [arr objectEnumerator];
105            NSData *d;
106            struct addrinfo *a;
107            ai = malloc(sizeof(struct addrinfo));
108            a = ai;
109            while (d = [e nextObject])
110            {
111                   struct sockaddr *s = (struct sockaddr*)[d bytes];
112                   a->ai_family = s->sa_family;
113                   a->ai_addr = s;
114                   a->ai_next = malloc(sizeof(struct addrinfo));
115                   a = a->ai_next;
116            }
117            }
118 }
119
120 -(void)disconnect
121 {
122     NSLog(@"Got a disconnect");
123     dieflag = 1;
124     do {} while (dieflag == 1);
125 }
126
127 -(void)retryConnection
128 {
129     ai_cur = ai_cur->ai_next?ai_cur->ai_next:ai_cur;
130     [self disconnect];
131     [self realDoConnection];
132 }
133
134 -(ITInetSocketState)state
135 {
136     return state;
137 }
138 -(id <ITInetSocketDelegate>)delegate
139 {
140     return delegate;
141 }
142
143 -(unsigned short)bufferSize
144 {
145     return bufs;
146 }
147
148 -(void)setBufferSize:(unsigned short)_bufs
149 {
150     bufs = _bufs;
151 }
152
153 -(void)newDataAdded:(ITByteStream*)sender
154 {
155     NSLog(@"writePipe got something");
156     actionflag = 1;
157     NSLog(@"thread saw actionFlag");
158 }
159 @end
160
161 @implementation ITInetSocket(Debugging)
162 -(NSString*)dumpv6Addrinfo:(struct addrinfo *)_ai
163 {
164     const char *cfmt =
165     "{\n"
166     "Flags = %x\n"
167     "Family = %x\n"
168     "Socktype = %x\n"
169     "Protocol = %x\n"
170     "Canonname = %s\n"
171     "Sockaddr = \n"
172     "\t{\n"
173     "\tLength = %x\n"
174     "\tFamily = %x\n"
175     "\tPort = %d\n"
176     "\tFlowinfo = %x\n"
177     "\tAddr = {%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx}\n"
178     "\tScope = %x\n"
179     "\t}\n"
180     "Next = %@\n"
181     "}\n";
182     NSString *nsfmt = [NSString stringWithCString:cfmt];
183     struct sockaddr_in6 *sa = (struct sockaddr_in6 *)_ai->ai_addr;
184     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"];
185
186     return buf;
187 }
188 @end
189
190 @implementation ITInetSocket(Private)
191 -(void)doConnSetupWithHost:(NSString*)host namedPort:(NSString*)namedPort
192 {
193     struct addrinfo hints;
194            int err;
195            const char *portNam = [namedPort cString], *hostCStr = [host cString];
196
197            hints.ai_flags = 0;
198            hints.ai_family = PF_UNSPEC;
199            hints.ai_socktype = SOCK_STREAM;
200            hints.ai_protocol = IPPROTO_TCP;
201            hints.ai_addrlen = 0;
202            hints.ai_canonname = NULL;
203            hints.ai_addr = NULL;
204            hints.ai_next = NULL;
205
206            err = getaddrinfo(hostCStr,portNam,&hints,&ai);
207
208            NSLog(@"%s, h %@ p %@",gai_strerror(err),host,namedPort);
209            NSLog(ai?[self dumpv6Addrinfo:ai]:@"");
210            ai_cur = ai;
211            [self realDoConnection];
212 }
213
214 -(void)realDoConnection
215 {
216     sockfd = socket(ai_cur->ai_addr->sa_family,SOCK_STREAM,IPPROTO_TCP);
217     [self spinoffReadLoop];
218 }
219
220 -(void)spinoffReadLoop
221 {
222     NSPort *p1 = [NSPort port], *p2 = [NSPort port];
223     NSConnection *dcon = [[NSConnection alloc] initWithReceivePort:p1 sendPort:p2];
224     NSArray *par = [NSArray arrayWithObjects:p2,p1,nil];
225     [dcon setRootObject:delegate];
226     [NSThread detachNewThreadSelector:@selector(socketReadLoop:) toTarget:self withObject:par]; //spawn read thread
227 }
228
229 -(void)socketReadLoop:(id)data
230 {
231     NSAutoreleasePool *ap = [[NSAutoreleasePool alloc] init];
232     NSConnection *dcon = [[NSConnection alloc] initWithReceivePort:[data objectAtIndex:0] sendPort:[data objectAtIndex:1]];
233     NSProxy *dp = [[dcon rootProxy] retain];
234     char *buf = malloc(bufs);
235     unsigned long readLen = 0;
236     signed int err;
237     [readPipe setDelegate:dp];
238     NSLog(@"Connecting");
239     err = connect(sockfd,ai_cur->ai_addr,ai_cur->ai_addrlen);
240     if (err == -1)
241            {
242            perror("CAwh");
243            [(id)dp errorOccured:ITInetCouldNotConnect during:ITInetSocketConnecting onSocket:self];
244            goto dieaction;
245            }
246     NSLog(@"Sending finishedConnecting");
247     [(id)dp finishedConnecting:self];
248 lstart:
249
250            while (!actionflag && !dieflag)
251            {
252                   NSData *d;
253                   readLen = recv(sockfd,buf,bufs,0);
254                   if (readLen == -1) {[(id)dp errorOccured:ITInetConnectionDropped during:ITInetSocketReading onSocket:self];goto dieaction;}
255                   if (readLen) {
256                          NSLog(@"recv'd");
257                          NSLog(@"Doing writeData to readPipe");
258                          [readPipe writeBytes:buf len:readLen];
259                          [ap release];
260                          ap = [[NSAutoreleasePool alloc] init];
261                   }
262            }
263
264     actionflag = 0;
265
266     if (dieflag)
267            {
268 dieaction:
269            perror("Awh");
270            free(buf);
271            shutdown(sockfd,2);
272            [dcon release];
273            [dp release];
274            [ap release];
275            dieflag = 0;
276            return;
277            }
278
279     {
280            NSLog(@"Emptying writePipe");
281            NSData *d = [writePipe readAllData];
282            write(sockfd,[d bytes],[d length]);
283            [ap release];
284            ap = [[NSAutoreleasePool alloc] init];
285            goto lstart;
286            }
287     goto dieaction;
288 }
289 @end