Added an ITFoundationTester. Why doesn't it work inside PB? :\
[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 <arpa/inet.h>
13 #import <netinet/in.h>
14 #import <sys/socket.h>
15 #import <netdb.h>
16 #import <fcntl.h>
17 #import <unistd.h>
18
19 /* Too bad Objective-C doesn't have class variables... */
20 static NSMutableDictionary *servsockets;
21 static NSTimer *timer;
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)setupTimer;
32 +(void)stopTimer;
33 +(void)globalTimerFunc:(NSTimer*)timer;
34 -(BOOL)timerFunc;
35 @end
36
37 @implementation ITInetServerSocket
38 + (void)initialize
39 {
40     servsockets = [[NSMutableDictionary 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            rndType = rndName = nil;
54            }
55     return self;
56 }
57
58 - (id)initWithDelegate:(id)d
59 {
60     if (self = [super init])
61            {
62            sockfd = -1;
63            delegate = [d retain];
64            clients = [[NSMutableSet alloc] init];
65            service = nil;
66            port = 0;
67            rndType = rndName = nil;
68            }
69     return self;
70 }
71
72 - (void)dealloc
73 {
74     [self stopConnection];
75     [clients release];
76     [delegate release];
77     [rndName release];
78     [rndType release];
79 }
80
81 - (BOOL)start
82 {
83     if (!rndName || !rndType || !port) return NO;
84     [ITInetServerSocket registerSocket:self];
85     return YES;
86 }
87
88 - (int)sockfd
89 {
90     return sockfd;
91 }
92
93 - (NSSet*)clients
94 {
95     return clients;
96 }
97
98 - (id)delegate
99 {
100     return delegate;
101 }
102
103 - (short)port
104 {
105     return port;
106 }
107
108 - (void)stop
109 {
110     [ITInetServerSocket unregisterSocket:self];
111 }
112
113 - (void)setServiceType:(NSString*)type useForPort:(BOOL)p
114 {
115     rndType = [type retain];
116     if (p) {
117            port = [self lookupPortForServiceType:type];
118     }
119 }
120
121 - (void)setServiceName:(NSString*)name
122 {
123     rndName = [name retain];
124 }
125
126 - (void)setPort:(short)p
127 {
128     port = p;
129 }
130 @end
131
132 @implementation ITInetServerSocket(Private)
133 +(void)registerSocket:(ITInetServerSocket*)sock
134 {
135     [sock setupConnection];
136     [servsockets setObject:sock forKey:[NSString stringWithFormat:@"%lu",[sock port]]];
137 }
138
139 +(void)unregisterSocket:(ITInetServerSocket*)sock
140 {
141     [sock stopConnection];
142     [servsockets removeObjectForKey:[NSString stringWithFormat:@"%lu",[sock port]]];
143 }
144
145 -(short)lookupPortForServiceType:(NSString*)name
146 {
147     const char *_name = [name cString];
148     struct addrinfo hints,*res;
149     short p;
150
151     hints.ai_flags = AI_PASSIVE;
152     hints.ai_family = PF_INET;
153     hints.ai_socktype = SOCK_STREAM;
154     hints.ai_protocol = IPPROTO_TCP;
155     hints.ai_addrlen = 0;
156     hints.ai_canonname = NULL;
157     hints.ai_addr = NULL;
158     hints.ai_next = NULL;
159
160     getaddrinfo(NULL,_name,&hints,&res);
161     p = ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port);
162     freeaddrinfo(res);
163     return p;
164 }
165
166 -(void)setupConnection
167 {
168     struct sockaddr_in sa;
169
170     sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
171     sa.sin_addr.s_addr = INADDR_ANY;
172     sa.sin_family = AF_INET;
173     sa.sin_port = htons(port);
174     bind(sockfd,(struct sockaddr *)&sa,sizeof(sa));
175     listen(sockfd, SOMAXCONN);
176     fcntl(sockfd,F_SETFL,O_NONBLOCK);
177     [self setupRendezvousAdvertising];
178 }
179
180 - (void)stopConnection
181 {
182     [self stopRendezvousAdvertising];
183     [clients makeObjectsPerformSelector:@selector(disconnect)];
184     shutdown(sockfd,2);
185     close(sockfd);
186     sockfd = -1;
187 }
188
189 - (void)setupRendezvousAdvertising
190 {
191     service = [[NSNetService alloc] initWithDomain:@"" type:[NSString stringWithFormat:@"_%@._tcp.",rndType] name:rndName port:htons(port)];
192     [service publish];
193 }
194
195 - (void)stopRendezvousAdvertising
196 {
197     [service stop];
198     [service release];
199     service = nil;
200 }
201
202 + (void)setupTimer
203 {
204     if (!timer) timer = [NSTimer timerWithTimeInterval:0 target:self selector:@selector(globalTimerFunc:) userInfo:nil repeats:YES];
205     [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
206 }
207
208 + (void)stopTimer
209 {
210     [timer invalidate];
211     [timer release];
212     timer = nil;
213 }
214
215 + (void)globalTimerFunc:(NSTimer*)timer
216 {
217     NSEnumerator *enume = [servsockets objectEnumerator];
218     id obj;
219
220     while (obj = [enume nextObject])
221            {
222            [obj timerFunc];
223            }
224 }
225
226 - (BOOL)timerFunc
227 {
228     if (sockfd != -1)
229            {
230            struct sockaddr_in csa;
231            int csalen;
232            signed int cfd;
233            cfd = accept(sockfd,(struct sockaddr*)&csa,&csalen);
234            if (cfd == -1) {
235                   if (errno == EWOULDBLOCK) return NO;
236                   else {perror("Too bad I haven't implemented error checking yet");}
237            }
238            else {
239                   ITInetSocket *csocket = [[[ITInetSocket alloc] initWithFD:cfd delegate:self] autorelease];
240                   [clients addObject:csocket];
241                   [delegate newClientJoined:csocket];
242                   return YES;
243            }
244            }
245     return NO;
246 }
247 @end