]> err.no Git - sope/blob - sope-core/NGStreams/NGInternetSocketAddress.m
minor code cleanups
[sope] / sope-core / NGStreams / NGInternetSocketAddress.m
1 /*
2   Copyright (C) 2000-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
6   OGo is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with OGo; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #include "common.h"
23
24 #if HAVE_SYS_TYPES_H || defined(__APPLE__)
25 #  include <sys/types.h>
26 #endif
27 #if HAVE_NETINET_IN_H
28 #  include <netinet/in.h>
29 #endif
30 #if HAVE_UNISTD_H || defined(__APPLE__)
31 #  include <unistd.h>
32 #endif
33 #if defined(__APPLE__)
34 #  include <netdb.h>
35 #endif
36
37 #if !defined(__CYGWIN32__)
38 #  if HAVE_WINDOWS_H
39 #    include <windows.h>
40 #  endif
41 #  if HAVE_WINSOCK_H
42 #    include <winsock.h>
43 #  endif
44 #endif
45
46 #include "NGSocketExceptions.h"
47 #include "NGInternetSocketAddress.h"
48 #include "NGInternetSocketDomain.h"
49 #include "common.h"
50
51 #if defined(HAVE_GETHOSTBYNAME_R) && !defined(linux)
52 #define USE_GETHOSTBYNAME_R 1
53 #endif
54
55 @implementation NGInternetSocketAddress
56
57 #if LIB_FOUNDATION_LIBRARY
58 extern NSRecursiveLock *libFoundationLock;
59 #define systemLock libFoundationLock
60 #else
61 static NSRecursiveLock *systemLock = nil;
62 #endif
63
64 static NSMapTable *nameCache = NULL;
65
66 + (void)initialize {
67   [NGSocket initialize];
68   
69   if (nameCache == NULL) {
70     nameCache = NSCreateMapTable(NSIntMapKeyCallBacks,
71                                  NSObjectMapValueCallBacks,
72                                  128);
73   }
74   
75 #if !LIB_FOUNDATION_LIBRARY
76   [[NSNotificationCenter defaultCenter]
77                          addObserver:self selector:@selector(taskNowMultiThreaded:)
78                          name:NSWillBecomeMultiThreadedNotification
79                          object:nil];
80 #endif
81 }
82
83 + (void)taskNowMultiThreaded:(NSNotification *)_notification {
84   if (systemLock == nil) systemLock = [[NSRecursiveLock alloc] init];
85 }
86
87 static inline NSString *_nameOfLocalhost(void) {
88 #if 1
89   return [[NSHost currentHost] name];
90 #else
91   NSString *hostName = nil;
92
93   [systemLock lock];
94   {
95     char buffer[1024];
96     gethostname(buffer, sizeof(buffer));
97     hostName = [[NSString alloc] initWithCString:buffer];
98   }
99   [systemLock unlock];
100
101   return [hostName autorelease];
102 #endif
103 }
104
105 - (void)_fillHost {
106   /*
107     Fill up the host and port ivars based on the INET address.
108     
109     TODO: cache some information, takes quite some time (11% of execution
110     time on MacOSX proftest) to get the hostname of an address.
111   */
112   struct hostent *hostEntity = NULL; // only valid during lock
113   NSString       *newHost  = nil;
114   int            errorCode = 0;
115   struct sockaddr_in *sockAddr = self->address;
116   
117   if (self->isHostFilled)
118     /* host is already filled .. */
119     return;
120
121 #if DEBUG
122   NSAssert(self->isAddressFilled, @"either host or address must be filled ...");
123 #endif
124   
125   if (sockAddr->sin_addr.s_addr != 0) { // not a wildcard address
126 #if !defined(HAVE_GETHOSTBYADDR_R)
127     [systemLock lock];
128     newHost = NSMapGet(nameCache, (void *)sockAddr->sin_addr.s_addr);
129 #else
130     [systemLock lock];
131     newHost = NSMapGet(nameCache, (void *)sockAddr->sin_addr.s_addr);
132     [systemLock unlock];
133 #endif
134     if (newHost == nil) { 
135       BOOL done = NO;
136       
137       while (!done) {
138 #if USE_GETHOSTBYNAME_R
139         struct hostent hostEntityBuffer;
140         char buffer[8200];
141         
142         hostEntity = gethostbyaddr_r((char *)&(sockAddr->sin_addr.s_addr),
143                                      4,
144                                      [[self domain] socketDomain],
145                                      &hostEntityBuffer,
146                                      buffer, 8200,
147                                      &errorCode);
148 #else
149 # ifdef __MINGW32__
150 #   warning "doesn't resolve host name on mingw32 !"
151         hostEntity = NULL;
152         errorCode  = -1;
153 # else
154         hostEntity = gethostbyaddr((char *)&(sockAddr->sin_addr.s_addr),
155                                    4,
156                                    [[self domain] socketDomain]);
157 #  if defined(WIN32) && !defined(__CYGWIN32__)
158         errorCode = WSAGetLastError();
159 #  else
160         errorCode = h_errno;
161 #  endif
162 # endif
163 #endif
164         if (hostEntity == NULL) {
165           done = YES;
166           
167           switch (errorCode) {
168 #ifdef __MINGW32__
169             case -1:
170               break;
171 #endif
172             case HOST_NOT_FOUND:
173               NSLog(@"%s: host not found ..", __PRETTY_FUNCTION__);
174               break;
175               
176             case TRY_AGAIN:
177 #ifndef __linux
178               NSLog(@"%s:\n  couldn't lookup host, retry ..",
179                     __PRETTY_FUNCTION__);
180               done = NO;
181 #else
182               NSLog(@"%s: couldn't lookup host ..", __PRETTY_FUNCTION__);
183 #endif
184               break;
185             
186             case NO_RECOVERY:
187               NSLog(@"%s: no recovery", __PRETTY_FUNCTION__);
188               break;
189             
190             case NO_DATA:
191               NSLog(@"%s: no data", __PRETTY_FUNCTION__);
192               break;
193             
194             default:
195               NSLog(@"%s: unknown error: h_errno=%i errno=%s",
196                     __PRETTY_FUNCTION__,
197                     errorCode, strerror(errno));
198               break;
199           }
200           
201           newHost = [NSString stringWithCString:inet_ntoa(sockAddr->sin_addr)];
202         }
203         else {
204           newHost = [NSString stringWithCString:hostEntity->h_name];
205           done = YES;
206         }
207       }
208
209       if (hostEntity == NULL) {
210         // throw could not get address ..
211         NSLog(@"could not get DNS name of address %@ in domain %@: %i",
212               newHost, [self domain], errorCode);
213       }
214       else if (newHost) {
215         /* add to cache */
216         NSMapInsert(nameCache, (void *)sockAddr->sin_addr.s_addr, newHost);
217       }
218       /* TODO: should also cache unknown IPs ! */
219     }
220     
221     //else printf("%s: CACHE HIT !\n", __PRETTY_FUNCTION__);
222     
223 #if !defined(HAVE_GETHOSTBYADDR_R)
224     [systemLock unlock];
225 #endif
226   }
227   else {
228     /* wildcard address */
229     newHost = nil;
230   }
231
232   ASSIGNCOPY(self->hostName, newHost);
233   self->isHostFilled = YES;
234 }
235
236 - (NSException *)_fillAddress {
237   /*
238     Fill up the INET address based on the host and port ivars.
239   */
240   // throws
241   //   NGCouldNotResolveHostNameException  when a DNS lookup fails
242   
243 #if defined(WIN32) && !defined(__CYGWIN32__)
244   u_long *ia = &(((struct sockaddr_in *)self->address)->sin_addr.s_addr);
245 #else
246   unsigned int *ia = &(((struct sockaddr_in *)self->address)->sin_addr.s_addr);
247 #endif
248   
249   if (self->isAddressFilled)
250     /* address is already filled .. */
251     return nil;
252   
253 #if DEBUG
254   NSAssert(self->isHostFilled, @"either host or address must be filled ...");
255 #endif
256   
257   if (self->hostName == nil) {
258     //  if ([self isWildcardAddress])
259     *ia = htonl(INADDR_ANY); // wildcard (0)
260     self->isAddressFilled = YES;
261   }
262   else {
263     const unsigned char *chost;
264     
265     chost = [[self hostName] cString];
266     
267     // try to interpret hostname as INET dotted address (eg 122.133.44.87)
268     *ia = inet_addr(chost);
269     
270     if ((int)*ia != -1) { // succeeded
271       self->isAddressFilled = YES;
272     }
273     else { // failed, try to interpret hostname as DNS hostname
274       BOOL didFail   = NO;
275       int  errorCode = 0;
276       int  addrType  = AF_INET;
277 #if defined(HAVE_GETHOSTBYNAME_R) && !defined(linux)
278       char buffer[4096];
279       struct hostent hostEntity;
280 #else
281       struct hostent *hostEntity; // only valid during lock
282 #endif
283
284 #if defined(HAVE_GETHOSTBYNAME_R) && !defined(linux)
285       if (gethostbyname_r(chost, &hostEntity,
286                           buffer, sizeof(buffer), &errorCode) == NULL) {
287         didFail = YES;
288       }
289       else {
290         addrType = hostEntity.h_addrtype;
291
292         if (addrType == AF_INET)
293           *ia = ((struct in_addr *)(hostEntity.h_addr_list[0]))->s_addr;
294         else
295           didFail = YES; // invalid domain (eg AF_INET6)
296       }
297 #else
298       [systemLock lock];
299       {
300         if ((hostEntity = gethostbyname(chost)) == NULL) {
301           didFail = YES;
302 #if defined(WIN32) && !defined(__CYGWIN32__)
303           errorCode = WSAGetLastError();
304 #else
305           errorCode = h_errno;
306 #endif
307         }
308         else {
309           addrType = hostEntity->h_addrtype;
310           
311           if (addrType == AF_INET)
312             *ia = ((struct in_addr *)(hostEntity->h_addr_list[0]))->s_addr;
313           else
314             didFail = YES; // invalid domain (eg AF_INET6)
315         }
316       }
317       [systemLock unlock];
318 #endif
319
320       if (didFail) { // could not resolve hostname
321         // did not find host
322         NSString *reason = nil;
323
324         if (addrType != AF_INET) {
325           // invalid domain (eg AF_INET6)
326           reason = @"resolved address is in invalid domain";
327         }
328         else {
329           switch (errorCode) {
330             case HOST_NOT_FOUND: reason = @"host not found"; break;
331             case TRY_AGAIN:      reason = @"try again";      break;
332             case NO_RECOVERY:    reason = @"no recovery";    break;
333             case NO_DATA:        reason = @"no address available"; break;
334             default:
335               reason = [NSString stringWithFormat:@"error code %i", errorCode];
336               break;
337           }
338         }
339         return [[[NGCouldNotResolveHostNameException alloc]
340                   initWithHostName:[self hostName] reason:reason] autorelease];
341       }
342
343       self->isAddressFilled = YES;
344     }
345   }
346   return nil;
347 }
348
349 /* constructors */
350
351 + (id)addressWithPort:(int)_port onHost:(id)_host {
352   return [[[self alloc] initWithPort:_port onHost:_host] autorelease];
353 }
354 + (id)addressWithPort:(int)_port {
355   return [[[self alloc] initWithPort:_port] autorelease];
356 }
357
358 + (id)addressWithService:(NSString *)_sname onHost:(id)_host
359   protocol:(NSString *)_protocol
360 {
361   return [[[self alloc] initWithService:_sname
362                         onHost:_host
363                         protocol:_protocol]
364                         autorelease];
365 }
366 + (id)addressWithService:(NSString *)_sname protocol:(NSString *)_protocol {
367   return [[[self alloc] initWithService:_sname protocol:_protocol] autorelease];
368 }
369
370 + (id)wildcardAddress {
371   return [[[self alloc] initWithPort:0 onHost:@"*"] autorelease];
372 }
373 + (id)wildcardAddressWithPort:(int)_port {
374   return [[[self alloc] initWithPort:_port onHost:@"*"] autorelease];
375 }
376
377 - (id)init {
378   if ((self = [super init])) {
379     self->address = malloc(sizeof(struct sockaddr_in));
380   }
381   return self;
382 }
383
384 - (id)initWithPort:(int)_port onHost:(id)_host { /* designated initializer */
385   if ((self = [self init])) {
386     self->isAddressFilled = NO;
387     self->isHostFilled    = YES;
388     
389     if (_host != nil) {
390       if ([_host isKindOfClass:[NSHost class]])
391         _host = [(NSHost *)_host address];
392       
393       if ([_host isEqualToString:@"*"]) {
394         self->hostName = nil; /* wildcard host */
395       }
396       else {
397         self->hostName = [_host copy];
398         self->isWildcardHost = NO;
399       }
400     }
401     else {
402       /* wildcard host */
403       self->isWildcardHost = YES;
404     }
405     
406     ((struct sockaddr_in *)self->address)->sin_family =
407       [[self domain] socketDomain];
408     ((struct sockaddr_in *)self->address)->sin_port =
409       htons((short)(_port & 0xffff));
410   }
411   return self;
412 }
413
414 - (id)initWithService:(NSString *)_serviceName onHost:(id)_host
415   protocol:(NSString *)_protocol
416 {
417   NSException *exc = nil;
418   int port = -1;
419 #if defined(HAVE_GETSERVBYNAME_R)
420   char   buffer[2048];
421   struct servent entry;
422 #else
423   struct servent *entry;
424 #endif
425   
426 #if defined(HAVE_GETSERVBYNAME_R)
427   if (getservbyname_r((char *)[_serviceName cString], [_protocol cString],
428                       &entry, buffer, sizeof(buffer)) == NULL) {
429     exc = [[NGDidNotFindServiceException alloc] initWithServiceName:_serviceName];
430   }
431   else
432     port = entry.s_port;
433 #else
434   [systemLock lock];
435   {
436     entry = getservbyname((char *)[_serviceName cString], [_protocol cString]);
437     if (entry == NULL)
438       exc = [[NGDidNotFindServiceException alloc] initWithServiceName:_serviceName];
439     else
440       port = entry->s_port;
441   }
442   [systemLock unlock];
443 #endif
444
445   if (exc) {
446     self = [self autorelease];
447     [exc raise];
448     return nil;
449   }
450   return [self initWithPort:port onHost:_host];
451 }
452
453 - (id)initWithPort:(int)_port {
454   return [self initWithPort:_port onHost:_nameOfLocalhost()];
455 }
456
457 - (id)initWithService:(NSString *)_serviceName protocol:(NSString *)_protocol {
458   return [self initWithService:_serviceName
459                onHost:_nameOfLocalhost()
460                protocol:_protocol];
461 }
462
463 - (id)initWithDomain:(id)_domain
464   internalRepresentation:(void *)_representation
465   size:(int)_length
466 {
467   struct sockaddr_in *sockAddr = _representation;
468 #if DEBUG
469   NSAssert(_length == sizeof(struct sockaddr_in),
470            @"invalid socket address length");
471 #else
472   if (_length != sizeof(struct sockaddr_in)) {
473     NSLog(@"%s: got invalid sockaddr_in size ...", __PRETTY_FUNCTION__);
474     [self release];
475     return nil;
476   }
477 #endif
478   
479   if ((self = [self init]) == nil)
480     return nil;
481   
482   self->isHostFilled = NO; /* need to lookup DNS */
483   
484   /* fill address */
485   
486   self->isAddressFilled = YES;
487   memcpy(self->address, _representation, sizeof(struct sockaddr_in));
488   
489   if (sockAddr->sin_addr.s_addr != 0) {
490     /* not a wildcard address */
491     self->isWildcardHost = NO;
492   }
493   else {
494     /* wildcard address */
495     self->hostName       = nil;
496     self->isWildcardHost = YES;
497     self->isHostFilled   = YES; /* wildcard host, no DNS lookup ... */
498   }
499   
500   return self;
501 }
502
503 - (void)dealloc {
504   [self->hostName release];
505   if (self->address) free(self->address);
506   [super dealloc];
507 }
508
509 /* accessors */
510
511 - (NSString *)hostName {
512   if (!self->isHostFilled) [self _fillHost];
513   return [[self->hostName copy] autorelease];
514 }
515 - (NSString *)address {
516 #if defined(WIN32) && !defined(__CYGWIN32__)
517   u_long *ia;
518   ia = (u_long *)&(((struct sockaddr_in *)self->address)->sin_addr.s_addr);
519 #else
520   unsigned int ia;
521   ia = (unsigned int)&(((struct sockaddr_in *)self->address)->sin_addr.s_addr);
522 #endif
523
524   if (self->hostName == nil) /* wildcard */
525     return nil;
526   
527   if (!self->isAddressFilled)
528     [[self _fillAddress] raise];
529
530   {
531     char     *ptr = NULL;
532     NSString *str = nil;
533     
534     [systemLock lock];
535     {
536       ptr = inet_ntoa(*((struct in_addr *)ia));
537       str = [NSString stringWithCString:ptr];
538     }
539     [systemLock unlock];
540
541     return str;
542   }
543 }
544
545 - (int)port {
546   /* how to do ? */
547   if (!self->isAddressFilled)
548     [[self _fillAddress] raise];
549   return ntohs(((struct sockaddr_in *)self->address)->sin_port);
550 }
551
552 - (BOOL)isWildcardAddress {
553   if (self->isWildcardHost) return YES;
554   return ([self hostName] == nil) || ([self port] == 0);
555 }
556
557 /* NGSocketAddress protocol */
558
559 - (void *)internalAddressRepresentation {
560   // throws
561   //   NGCouldNotResolveHostNameException  when a DNS lookup fails
562   
563   if (!self->isAddressFilled)
564     [[self _fillAddress] raise];
565   
566   return self->address;
567 }
568
569 - (int)addressRepresentationSize {
570   return [[self domain] addressRepresentationSize];
571 }
572 - (id)domain {
573   static id domain = nil;
574   if (domain == nil) domain = [[NGInternetSocketDomain domain] retain];
575   return domain;
576 }
577
578 /* comparing */
579
580 - (unsigned)hash {
581   return [self port];
582 }
583
584 - (BOOL)isEqualToAddress:(NGInternetSocketAddress *)_otherAddress {
585   if (self == _otherAddress)
586     return YES;
587   if (![[_otherAddress hostName] isEqualToString:[self hostName]])
588     return NO;
589   if ([_otherAddress port] != [self port])
590     return NO;
591   return YES;
592 }
593
594 - (BOOL)isEqual:(id)_object {
595   if (_object == self) return YES;
596   if ([_object class] != [self class]) return NO;
597   return [self isEqualToAddress:_object];
598 }
599
600 /* NSCopying */
601
602 - (id)copyWithZone:(NSZone *)_zone {
603   // socket addresses are immutable, therefore just retain self
604   return [self retain];
605 }
606
607 /* NSCoding */
608
609 - (void)encodeWithCoder:(NSCoder *)_encoder {
610   int aPort = [self port];
611   
612   [_encoder encodeValueOfObjCType:@encode(int) at:&aPort];
613   [_encoder encodeObject:[self hostName]];
614 }
615 - (id)initWithCoder:(NSCoder *)_decoder {
616   int aPort;
617   id  aHost;
618
619   [_decoder decodeValueOfObjCType:@encode(int) at:&aPort];
620   aHost = [_decoder decodeObject];
621
622   return [self initWithPort:aPort onHost:aHost];
623 }
624
625 /* description */
626
627 - (NSString *)stringValue {
628   NSString *name;
629   
630   if ((name = [self hostName]) == nil)
631     name = @"*";
632   
633   return [NSString stringWithFormat:@"%@:%i", name, [self port]];
634 }
635
636 - (NSString *)description {
637   NSMutableString *ms;
638   id tmp;
639
640   ms = [NSMutableString stringWithCapacity:128];
641   [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
642   
643   if ((tmp = [self hostName]) != nil)
644     [ms appendFormat:@" host=%@", tmp];
645   else
646     [ms appendString:@" *host"];
647   
648   if (!self->isAddressFilled)
649     [ms appendString:@" not-filled"];
650   else
651     [ms appendFormat:@" port=%d", [self port]];
652   
653   [ms appendString:@">"];
654   return ms;
655 }
656
657 @end /* NGInternetSocketAddress */
658
659 @implementation NGActiveSocket(NGInternetActiveSocket)
660
661 + (id)socketConnectedToPort:(int)_port onHost:(id)_host {
662   // this method calls +socketConnectedToAddress: with an 
663   // NGInternetSocketAddress
664   
665   return [self socketConnectedToAddress:
666                  [NGInternetSocketAddress addressWithPort:_port onHost:_host]];
667 }
668
669 - (BOOL)connectToPort:(int)_port onHost:(id)_host {
670   // this method calls -connectToAddress: with an NGInternetSocketAddress
671   
672   return [self connectToAddress:
673                  [NGInternetSocketAddress addressWithPort:_port onHost:_host]];
674 }
675
676 @end /* NGActiveSocket(NGInternetActiveSocket) */