Sockets don't quite work, but they will almost work.
[ITFoundation.git] / ITInetServerSocket.m
1 //
2 //  ITInetServerSocket.m
3 //  ITFoundation
4 //
5 //  Created by Alexander Strange on Thu Feb 13 2003.
6 //  Copyright (c) 2003 __MyCompanyName__. All rights reserved.
7 //
8
9 #import "ITInetServerSocket.h"
10 #import "ITInetSocket.h"
11 #import <sys/types.h>
12 #import <sys/time.h>
13 #import <arpa/inet.h>
14 #import <netinet/in.h>
15 #import <sys/socket.h>
16 #import <netdb.h>
17 #import <fcntl.h>
18 #import <unistd.h>
19
20 /* Too bad Objective-C doesn't have class variables... */
21 static NSMutableSet *servsockets;
22
23 @interface ITInetServerSocket(Private)
24 +(void)registerSocket:(ITInetServerSocket*)sock;
25 +(void)unregisterSocket:(ITInetServerSocket*)sock;
26 -(short)lookupPortForServiceType:(NSString*)name;
27 -(void)setupConnection;
28 -(void)stopConnection;
29 -(void)setupRendezvousAdvertising;
30 -(void)stopRendezvousAdvertising;
31 -(void)setupThread;
32 -(void)stopThread;
33 -(void)newClient:(int)cfd;
34 -(void)socketAcceptLoop:(id)data;
35 @end
36
37 @implementation ITInetServerSocket
38 + (void)initialize
39 {
40     servsockets = [[NSMutableSet alloc] init];
41     //[self setupTimer];
42 }
43
44 - (id)init
45 {
46     if (self = [super init])
47            {
48            sockfd = -1;
49            delegate = nil;
50            clients = [[NSMutableSet alloc] init];
51            service = nil;
52            port = 0;
53            dieflag = 0;
54            rndType = rndName = nil;
55            timer = nil;
56            }
57     return self;
58 }
59
60 - (id)initWithDelegate:(id)d
61 {
62     if (self = [super init])
63            {
64            sockfd = -1;
65            delegate = [d retain];
66            clients = [[NSMutableSet alloc] init];
67            service = nil;
68            port = 0;
69            dieflag = 0;
70            rndType = rndName = nil;
71            timer = nil;
72            }
73     return self;
74 }
75
76 - (void)dealloc
77 {
78     [self stopConnection];
79     [clients release];
80     [delegate release];
81     [rndName release];
82     [rndType release];
83 }
84
85 - (BOOL)start
86 {
87     if (!rndName || !rndType || !port) return NO;
88     [ITInetServerSocket registerSocket:self];
89     return YES;
90 }
91
92 - (int)sockfd
93 {
94     return sockfd;
95 }
96
97 - (NSSet*)clients
98 {
99     return clients;
100 }
101
102 - (id)delegate
103 {
104     return delegate;
105 }
106
107 - (short)port
108 {
109     return port;
110 }
111
112 - (void)stop
113 {
114     [ITInetServerSocket unregisterSocket:self];
115 }
116
117 - (void)setServiceType:(NSString*)type useForPort:(BOOL)p
118 {
119     rndType = [type retain];
120     if (p) {
121            port = [self lookupPortForServiceType:type];
122     }
123 }
124
125 - (void)setServiceName:(NSString*)name
126 {
127     rndName = [name retain];
128 }
129
130 - (void)setPort:(short)p
131 {
132     port = p;
133 }
134 @end
135
136 @implementation ITInetServerSocket(Private)
137 +(void)registerSocket:(ITInetServerSocket*)sock
138 {
139     [sock setupConnection];
140     [servsockets addObject:sock];
141 }
142
143 +(void)unregisterSocket:(ITInetServerSocket*)sock
144 {
145     [sock stopConnection];
146     [servsockets removeObject:sock];
147 }
148
149 -(short)lookupPortForServiceType:(NSString*)name
150 {
151     const char *_name = [name cString];
152     struct addrinfo *res;
153     short p;
154     getaddrinfo(NULL,_name,NULL,&res);
155     p = ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port);
156     freeaddrinfo(res);
157     return p;
158 }
159
160 -(void)setupConnection
161 {
162     struct addrinfo hints, *ai;
163     hints.ai_flags = AI_PASSIVE;
164     hints.ai_family = PF_INET6;
165     hints.ai_socktype = SOCK_STREAM;
166     hints.ai_protocol = IPPROTO_TCP;
167     hints.ai_addrlen = 0;
168     hints.ai_canonname = hints.ai_addr = hints.ai_next = NULL;
169     getaddrinfo(NULL,[[[NSNumber numberWithShort:port] stringValue] cString],&hints,&ai);
170     bind(sockfd,ai->ai_addr,ai->ai_addrlen);
171     listen(sockfd, SOMAXCONN);
172     freeaddrinfo(ai);
173     [self setupRendezvousAdvertising];
174     [self setupThread];
175 }
176
177 - (void)stopConnection
178 {
179     [self stopRendezvousAdvertising];
180     [clients makeObjectsPerformSelector:@selector(disconnect)];
181     shutdown(sockfd,2);
182     close(sockfd);
183     sockfd = -1;
184 }
185
186 - (void)setupRendezvousAdvertising
187 {
188     NSString *a = [NSString stringWithFormat:@"_%@._tcp.",rndType];
189     service = [[NSNetService alloc] initWithDomain:@"" type:a name:rndName port:htons(port)];
190     NSLog(@"Advertising a service of type %@ name %@",a,rndName);
191     [service publish];
192 }
193
194 - (void)stopRendezvousAdvertising
195 {
196     [service stop];
197     [service release];
198     service = nil;
199 }
200
201 - (void)setupThread
202 {
203     NSPort *p1 = [NSPort port], *p2 = [NSPort port];
204     NSConnection *dcon = [[NSConnection alloc] initWithReceivePort:p1 sendPort:p2];
205     NSArray *par = [NSArray arrayWithObjects:p2,p1,nil];
206     [dcon setRootObject:self];
207     NSLog(@"detached server thread");
208     [NSThread detachNewThreadSelector:@selector(socketAcceptLoop:) toTarget:self withObject:par]; 
209 }
210
211 - (void)stopThread
212 {
213     dieflag = 1;
214 }
215
216 - (void)newClient:(int)cfd
217 {
218     ITInetSocket *csocket = [[ITInetSocket alloc] initWithFD:cfd delegate:delegate];
219     [clients addObject:csocket];
220 }
221
222 - (void)socketAcceptLoop:(id)data
223 {
224     NSAutoreleasePool *ap = [[NSAutoreleasePool alloc] init];
225     NSArray *par = data;
226     NSConnection *dcon = [[NSConnection alloc] initWithReceivePort:[par objectAtIndex:0] sendPort:[par objectAtIndex:1]];
227     NSProxy *dp = [dcon rootProxy];
228     while ((sockfd != -1) && !dieflag)
229            {
230            signed int cfd;
231            cfd = accept(sockfd,NULL,NULL);
232            NSLog(@"Someone connected!");
233            [(id)dp newClient:cfd];
234            }
235     dieflag = 0;
236     [dcon release];
237     [ap release];
238 }
239 @end