c51c05a06ff43b11050f01f9eb2e630d55ec1576
[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] init];
45            readPipe = [[ITByteStream alloc] init];
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] init];
63            readPipe = [[ITByteStream alloc] init];
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     dieflag = 1;
123     do {} while (dieflag == 1);
124 }
125
126 -(void)retryConnection
127 {
128     ai_cur = ai_cur->ai_next?ai_cur->ai_next:ai_cur;
129     [self disconnect];
130     [self realDoConnection];
131 }
132
133 -(ITInetSocketState)state
134 {
135     return state;
136 }
137 -(id <ITInetSocketDelegate>)delegate
138 {
139     return delegate;
140 }
141
142 -(unsigned short)bufferSize
143 {
144     return bufs;
145 }
146
147 -(void)setBufferSize:(unsigned short)_bufs
148 {
149     bufs = _bufs;
150 }
151 @end
152
153 @implementation ITInetSocket(Debugging)
154 -(NSString*)dumpv6Addrinfo:(struct addrinfo *)_ai
155 {
156     const char *cfmt =
157     "{\n"
158     "Flags = %x\n"
159     "Family = %x\n"
160     "Socktype = %x\n"
161     "Protocol = %x\n"
162     "Canonname = %s\n"
163     "Sockaddr = \n"
164     "\t{\n"
165     "\tLength = %x\n"
166     "\tFamily = %x\n"
167     "\tPort = %d\n"
168     "\tFlowinfo = %x\n"
169     "\tAddr = {%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx}\n"
170     "\tScope = %x\n"
171     "\t}\n"
172     "Next = %@\n"
173     "}\n";
174     NSString *nsfmt = [NSString stringWithCString:cfmt];
175     struct sockaddr_in6 *sa = (struct sockaddr_in6 *)_ai->ai_addr;
176     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"];
177
178     return buf;
179 }
180 @end
181
182 @implementation ITInetSocket(Private)
183 -(void)doConnSetupWithHost:(NSString*)host namedPort:(NSString*)namedPort
184 {
185     struct addrinfo hints;
186            int err;
187            const char *portNam = [namedPort cString], *hostCStr = [host cString];
188
189            hints.ai_flags = 0;
190            hints.ai_family = PF_UNSPEC;
191            hints.ai_socktype = SOCK_STREAM;
192            hints.ai_protocol = IPPROTO_TCP;
193            hints.ai_addrlen = 0;
194            hints.ai_canonname = NULL;
195            hints.ai_addr = NULL;
196            hints.ai_next = NULL;
197
198            err = getaddrinfo(hostCStr,portNam,&hints,&ai);
199
200            NSLog(@"%s, h %@ p %@",gai_strerror(err),host,namedPort);
201            NSLog(ai?[self dumpv6Addrinfo:ai]:@"");
202            ai_cur = ai;
203            [self realDoConnection];
204 }
205
206 -(void)realDoConnection
207 {
208     sockfd = socket(ai_cur->ai_addr->sa_family,SOCK_STREAM,IPPROTO_TCP);
209     [self spinoffReadLoop];
210 }
211
212 -(void)spinoffReadLoop
213 {
214     NSPort *p1 = [NSPort port], *p2 = [NSPort port];
215     NSConnection *dcon = [[NSConnection alloc] initWithReceivePort:p1 sendPort:p2];
216     NSArray *par = [NSArray arrayWithObjects:p2,p1,nil];
217     [dcon setRootObject:delegate];
218     [NSThread detachNewThreadSelector:@selector(socketReadLoop:) toTarget:self withObject:par]; //spawn read thread
219 }
220
221 -(void)socketReadLoop:(id)data
222 {
223     NSAutoreleasePool *ap = [[NSAutoreleasePool alloc] init];
224     NSConnection *dcon = [[NSConnection alloc] initWithReceivePort:[data objectAtIndex:0] sendPort:[data objectAtIndex:1]];
225     NSProxy *dp = [dcon rootProxy];
226     char *buf = malloc(bufs);
227     unsigned long readLen = 0;
228     signed int err;
229     NSLog(@"EYE MAEK CONNECT");
230     err = connect(sockfd,ai_cur->ai_addr,ai_cur->ai_addrlen);
231     if (err == -1)
232            {
233            perror("CAwh");
234            [(id)dp errorOccured:ITInetCouldNotConnect during:ITInetSocketConnecting onSocket:self];
235            goto dieaction;
236            }
237     [(id)dp finishedConnecting:self];
238 lstart:
239
240            while (!actionflag && ![writePipe availableDataLength] && !dieflag)
241            {
242                   NSData *d;
243                   readLen = recv(sockfd,buf,bufs,0);
244                   if (readLen) {
245                          d = [NSData alloc];
246                          [d initWithBytesNoCopy:buf length:readLen freeWhenDone:NO];
247                          [readPipe writeData:d];
248                          [d release];
249                          [(id)dp dataReceived:self];
250                   }
251            }
252
253     actionflag = 0;
254
255     if (dieflag)
256            {
257 dieaction:
258            perror("Awh");
259            free(buf);
260            shutdown(sockfd,2);
261            [dcon release];
262            [ap release];
263            dieflag = 0;
264            return;
265            }
266
267     {
268            NSData *d = [writePipe readAllData];
269            write(sockfd,[d bytes],[d length]);
270            goto lstart;
271            }
272     goto dieaction;
273 }
274 @end