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