Give me a B! Give me a U! Give me a G!
[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
22 @interface ITInetServerSocket(Private)
23 -(short)lookupPortForServiceType:(NSString*)name;
24 -(void)setupConnection;
25 -(void)stopConnection;
26 -(void)setupRendezvousAdvertising;
27 -(void)stopRendezvousAdvertising;
28 -(void)setupThread;
29 -(void)stopThread;
30 -(void)newClient:(int)cfd;
31 -(void)socketAcceptLoop:(id)data;
32 @end
33
34 @implementation ITInetServerSocket
35 - (id)init
36 {
37     if (self = [super init])
38            {
39            sockfd = -1;
40            delegate = nil;
41            clients = [[NSMutableSet alloc] init];
42            service = nil;
43            port = 0;
44            dieflag = 0;
45            rndType = rndName = nil;
46            timer = nil;
47            }
48     return self;
49 }
50
51 - (id)initWithDelegate:(id)d
52 {
53     if (self = [super init])
54            {
55            sockfd = -1;
56            delegate = [d retain];
57            clients = [[NSMutableSet alloc] init];
58            service = nil;
59            port = 0;
60            dieflag = 0;
61            rndType = rndName = nil;
62            timer = nil;
63            }
64     return self;
65 }
66
67 - (void)dealloc
68 {
69     [self stopConnection];
70     [clients release];
71     [delegate release];
72     [rndName release];
73     [rndType release];
74 }
75
76 - (BOOL)start
77 {
78     if (!rndName || !rndType || !port) return NO;
79     [self setupConnection];
80     return YES;
81 }
82
83 - (int)sockfd
84 {
85     return sockfd;
86 }
87
88 - (NSSet*)clients
89 {
90     return clients;
91 }
92
93 - (id)delegate
94 {
95     return delegate;
96 }
97
98 - (short)port
99 {
100     return port;
101 }
102
103 - (void)stop
104 {
105     [self stopConnection];
106 }
107
108 - (void)setServiceType:(NSString*)type useForPort:(BOOL)p
109 {
110     rndType = [type retain];
111     if (p) {
112            port = [self lookupPortForServiceType:type];
113     }
114 }
115
116 - (void)setServiceName:(NSString*)name
117 {
118     rndName = [name retain];
119 }
120
121 - (void)setPort:(short)p
122 {
123     port = p;
124 }
125 @end
126
127 @implementation ITInetServerSocket(Private)
128 -(short)lookupPortForServiceType:(NSString*)name
129 {
130     const char *_name = [name cString];
131     struct addrinfo *res;
132     short p;
133     getaddrinfo(NULL,_name,NULL,&res);
134     p = ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port);
135     freeaddrinfo(res);
136     return p;
137 }
138
139 -(void)setupConnection
140 {
141     struct addrinfo hints, *ai;
142     hints.ai_flags = AI_PASSIVE;
143     hints.ai_family = PF_INET6;
144     hints.ai_socktype = SOCK_STREAM;
145     hints.ai_protocol = IPPROTO_TCP;
146     hints.ai_addrlen = 0;
147     hints.ai_canonname = hints.ai_addr = hints.ai_next = NULL;
148     getaddrinfo(NULL,[[[NSNumber numberWithShort:port] stringValue] cString],&hints,&ai);
149     sockfd = socket(PF_INET6,SOCK_STREAM,IPPROTO_TCP);
150     bind(sockfd,ai->ai_addr,ai->ai_addrlen);
151     listen(sockfd, SOMAXCONN);
152     freeaddrinfo(ai);
153     [self setupRendezvousAdvertising];
154     [self setupThread];
155 }
156
157 - (void)stopConnection
158 {
159     [self stopRendezvousAdvertising];
160     [clients makeObjectsPerformSelector:@selector(disconnect)];
161     [self stopThread];
162     close(sockfd);
163     sockfd = -1;
164 }
165
166 - (void)setupRendezvousAdvertising
167 {
168     NSString *a = [NSString stringWithFormat:@"_%@._tcp.",rndType];
169     service = [[NSNetService alloc] initWithDomain:@"" type:a name:rndName port:htons(port)];
170     NSLog(@"Advertising a service of type %@ name %@",a,rndName);
171     [service publish];
172 }
173
174 - (void)stopRendezvousAdvertising
175 {
176     [service stop];
177     [service release];
178     service = nil;
179 }
180
181 - (void)setupThread
182 {
183     NSPort *p1 = [NSPort port], *p2 = [NSPort port];
184     NSConnection *dcon = [[NSConnection alloc] initWithReceivePort:p1 sendPort:p2];
185     NSArray *par = [NSArray arrayWithObjects:p2,p1,nil];
186     [dcon setRootObject:self];
187     NSLog(@"detached server thread");
188     [NSThread detachNewThreadSelector:@selector(socketAcceptLoop:) toTarget:self withObject:par]; 
189 }
190
191 - (void)stopThread
192 {
193     NSLog(@"stopping server thread");
194     dieflag = 1;
195     do {} while (dieflag == 1);
196 }
197
198 - (void)newClient:(int)cfd
199 {
200     ITInetSocket *csocket = [[ITInetSocket alloc] initWithFD:cfd delegate:delegate];
201     NSLog(@"new client for this server");
202     [clients addObject:csocket];
203 }
204
205 - (void)socketAcceptLoop:(id)data
206 {
207     NSAutoreleasePool *ap = [[NSAutoreleasePool alloc] init];
208     NSArray *par = data;
209     NSConnection *dcon = [[NSConnection alloc] initWithReceivePort:[par objectAtIndex:0] sendPort:[par objectAtIndex:1]];
210     NSProxy *dp = [dcon rootProxy];
211     while ((sockfd >= 0) && !dieflag)
212            {
213            signed int cfd;
214            cfd = accept(sockfd,NULL,NULL);
215            NSLog(@"Someone connected!");
216            [(id)dp newClient:cfd];
217            [ap release];
218            ap = [[NSAutoreleasePool alloc] init];
219            }
220     
221     NSLog(@"suiciding");
222     dieflag = 0;
223     [dcon release];
224     [ap release];
225 }
226 @end