]> err.no Git - sope/blob - sope-core/NGStreams/NGInternetSocketAddress.m
fixed an id string
[sope] / sope-core / NGStreams / NGInternetSocketAddress.m
1 /*
2   Copyright (C) 2000-2003 SKYRIX Software AG
3
4   This file is part of OGo
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 // $Id$
22
23 #include "common.h"
24
25 #if HAVE_SYS_TYPES_H || defined(__APPLE__)
26 #  include <sys/types.h>
27 #endif
28 #if HAVE_NETINET_IN_H
29 #  include <netinet/in.h>
30 #endif
31 #if HAVE_UNISTD_H || defined(__APPLE__)
32 #  include <unistd.h>
33 #endif
34 #if defined(__APPLE__)
35 #  include <netdb.h>
36 #endif
37
38 #if !defined(__CYGWIN32__)
39 #  if HAVE_WINDOWS_H
40 #    include <windows.h>
41 #  endif
42 #  if HAVE_WINSOCK_H
43 #    include <winsock.h>
44 #  endif
45 #endif
46
47 #include "NGSocketExceptions.h"
48 #include "NGInternetSocketAddress.h"
49 #include "NGInternetSocketDomain.h"
50 #include "common.h"
51
52 #if defined(HAVE_GETHOSTBYNAME_R) && !defined(linux)
53 #define USE_GETHOSTBYNAME_R 1
54 #endif
55
56 @implementation NGInternetSocketAddress
57
58 #if LIB_FOUNDATION_LIBRARY
59 extern NSRecursiveLock *libFoundationLock;
60 #define systemLock libFoundationLock
61 #else
62 static NSRecursiveLock *systemLock = nil;
63 #endif
64
65 static NSMapTable *nameCache = NULL;
66
67 + (void)initialize {
68   [NGSocket initialize];
69   
70   if (nameCache == NULL) {
71     nameCache = NSCreateMapTable(NSIntMapKeyCallBacks,
72                                  NSObjectMapValueCallBacks,
73                                  128);
74   }
75   
76 #if !LIB_FOUNDATION_LIBRARY
77   [[NSNotificationCenter defaultCenter]
78                          addObserver:self selector:@selector(taskNowMultiThreaded:)
79                          name:NSWillBecomeMultiThreadedNotification
80                          object:nil];
81 #endif
82 }
83
84 + (void)taskNowMultiThreaded:(NSNotification *)_notification {
85   if (systemLock == nil) systemLock = [[NSRecursiveLock alloc] init];
86 }
87
88 static inline NSString *_nameOfLocalhost(void) {
89 #if 1
90   return [[NSHost currentHost] name];
91 #else
92   NSString *hostName = nil;
93
94   [systemLock lock];
95   {
96     char buffer[1024];
97     gethostname(buffer, sizeof(buffer));
98     hostName = [[NSString alloc] initWithCString:buffer];
99   }
100   [systemLock unlock];
101
102   return [hostName autorelease];
103 #endif
104 }
105
106 - (void)_fillHost {
107   /*
108     Fill up the host and port ivars based on the INET address.
109     
110     TODO: cache some information, takes quite some time (11% of execution
111     time on MacOSX proftest) to get the hostname of an address.
112   */
113   struct hostent *hostEntity = NULL; // only valid during lock
114   NSString       *newHost  = nil;
115   int            errorCode = 0;
116   struct sockaddr_in *sockAddr = self->address;
117   
118   if (self->isHostFilled)
119     /* host is already filled .. */
120     return;
121
122 #if DEBUG
123   NSAssert(self->isAddressFilled, @"either host or address must be filled ...");
124 #endif
125   
126   if (sockAddr->sin_addr.s_addr != 0) { // not a wildcard address
127 #if !defined(HAVE_GETHOSTBYADDR_R)
128     [systemLock lock];
129     newHost = NSMapGet(nameCache, (void *)sockAddr->sin_addr.s_addr);
130 #else
131     [systemLock lock];
132     newHost = NSMapGet(nameCache, (void *)sockAddr->sin_addr.s_addr);
133     [systemLock unlock];
134 #endif
135     if (newHost == nil) { 
136       BOOL done = NO;
137       
138       while (!done) {
139 #if USE_GETHOSTBYNAME_R
140         struct hostent hostEntityBuffer;
141         char buffer[8200];
142         
143         hostEntity = gethostbyaddr_r((char *)&(sockAddr->sin_addr.s_addr),
144                                      4,
145                                      [[self domain] socketDomain],
146                                      &hostEntityBuffer,
147                                      buffer, 8200,
148                                      &errorCode);
149 #else
150 # ifdef __MINGW32__
151 #   warning "doesn't resolve host name on mingw32 !"
152         hostEntity = NULL;
153         errorCode  = -1;
154 # else
155         hostEntity = gethostbyaddr((char *)&(sockAddr->sin_addr.s_addr),
156                                    4,
157                                    [[self domain] socketDomain]);
158 #  if defined(WIN32) && !defined(__CYGWIN32__)
159         errorCode = WSAGetLastError();
160 #  else
161         errorCode = h_errno;
162 #  endif
163 # endif
164 #endif
165         if (hostEntity == NULL) {
166           done = YES;
167           
168           switch (errorCode) {
169 #ifdef __MINGW32__
170             case -1:
171               break;
172 #endif
173             case HOST_NOT_FOUND:
174               NSLog(@"%s: host not found ..", __PRETTY_FUNCTION__);
175               break;
176               
177             case TRY_AGAIN:
178 #ifndef __linux
179               NSLog(@"%s:\n  couldn't lookup host, retry ..",
180                     __PRETTY_FUNCTION__);
181               done = NO;
182 #else
183               NSLog(@"%s: couldn't lookup host ..", __PRETTY_FUNCTION__);
184 #endif
185               break;
186             
187             case NO_RECOVERY:
188               NSLog(@"%s: no recovery", __PRETTY_FUNCTION__);
189               break;
190             
191             case NO_DATA:
192               NSLog(@"%s: no data", __PRETTY_FUNCTION__);
193               break;
194             
195             default:
196               NSLog(@"%s: unknown error: h_errno=%i errno=%s",
197                     __PRETTY_FUNCTION__,
198                     errorCode, strerror(errno));
199               break;
200           }
201           
202           newHost = [NSString stringWithCString:inet_ntoa(sockAddr->sin_addr)];
203         }
204         else {
205           newHost = [NSString stringWithCString:hostEntity->h_name];
206           done = YES;
207         }
208       }
209
210       if (hostEntity == NULL) {
211         // throw could not get address ..
212         NSLog(@"could not get DNS name of address %@ in domain %@: %i",
213               newHost, [self domain], errorCode);
214       }
215       else if (newHost) {
216         /* add to cache */
217         NSMapInsert(nameCache, (void *)sockAddr->sin_addr.s_addr, newHost);
218       }
219       /* TODO: should also cache unknown IPs ! */
220     }
221     
222     //else printf("%s: CACHE HIT !\n", __PRETTY_FUNCTION__);
223     
224 #if !defined(HAVE_GETHOSTBYADDR_R)
225     [systemLock unlock];
226 #endif
227   }
228   else {
229     /* wildcard address */
230     newHost = nil;
231   }
232
233   ASSIGNCOPY(self->hostName, newHost);
234   self->isHostFilled = YES;
235 }
236
237 - (void)_fillAddress {
238   /*
239     Fill up the INET address based on the host and port ivars.
240   */
241   // throws
242   //   NGCouldNotResolveHostNameException  when a DNS lookup fails
243   
244 #if defined(WIN32) && !defined(__CYGWIN32__)
245   u_long *ia = &(((struct sockaddr_in *)self->address)->sin_addr.s_addr);
246 #else
247   unsigned int *ia = &(((struct sockaddr_in *)self->address)->sin_addr.s_addr);
248 #endif
249   
250   if (self->isAddressFilled)
251     /* address is already filled .. */
252     return;
253   
254 #if DEBUG
255   NSAssert(self->isHostFilled, @"either host or address must be filled ...");
256 #endif
257   
258   if (self->hostName == nil) {
259     //  if ([self isWildcardAddress])
260     *ia = htonl(INADDR_ANY); // wildcard (0)
261     self->isAddressFilled = YES;
262   }
263   else {
264     const unsigned char *chost;
265     
266     chost = [[self hostName] cString];
267     
268     // try to interpret hostname as INET dotted address (eg 122.133.44.87)
269     *ia = inet_addr(chost);
270     
271     if ((int)*ia != -1) { // succeeded
272       self->isAddressFilled = YES;
273     }
274     else { // failed, try to interpret hostname as DNS hostname
275       BOOL didFail   = NO;
276       int  errorCode = 0;
277       int  addrType  = AF_INET;
278 #if defined(HAVE_GETHOSTBYNAME_R) && !defined(linux)
279       char buffer[4096];
280       struct hostent hostEntity;
281 #else
282       struct hostent *hostEntity; // only valid during lock
283 #endif
284
285 #if defined(HAVE_GETHOSTBYNAME_R) && !defined(linux)
286       if (gethostbyname_r(chost, &hostEntity,
287                           buffer, sizeof(buffer), &errorCode) == NULL) {
288         didFail = YES;
289       }
290       else {
291         addrType = hostEntity.h_addrtype;
292
293         if (addrType == AF_INET)
294           *ia = ((struct in_addr *)(hostEntity.h_addr_list[0]))->s_addr;
295         else
296           didFail = YES; // invalid domain (eg AF_INET6)
297       }
298 #else
299       [systemLock lock];
300       {
301         if ((hostEntity = gethostbyname(chost)) == NULL) {
302           didFail = YES;
303 #if defined(WIN32) && !defined(__CYGWIN32__)
304           errorCode = WSAGetLastError();
305 #else
306           errorCode = h_errno;
307 #endif
308         }
309         else {
310           addrType = hostEntity->h_addrtype;
311           
312           if (addrType == AF_INET)
313             *ia = ((struct in_addr *)(hostEntity->h_addr_list[0]))->s_addr;
314           else
315             didFail = YES; // invalid domain (eg AF_INET6)
316         }
317       }
318       [systemLock unlock];
319 #endif
320
321       if (didFail) { // could not resolve hostname
322         // did not find host
323         NSString *reason = nil;
324
325         if (addrType != AF_INET) {
326           // invalid domain (eg AF_INET6)
327           reason = @"resolved address is in invalid domain";
328         }
329         else {
330           switch (errorCode) {
331             case HOST_NOT_FOUND: reason = @"host not found"; break;
332             case TRY_AGAIN:      reason = @"try again";      break;
333             case NO_RECOVERY:    reason = @"no recovery";    break;
334             case NO_DATA:        reason = @"no address available"; break;
335             default:
336               reason = [NSString stringWithFormat:@"error code %i", errorCode];
337               break;
338           }
339         }
340         [[[NGCouldNotResolveHostNameException alloc]
341                   initWithHostName:[self hostName] reason:reason] raise];
342       }
343
344       self->isAddressFilled = YES;
345     }
346   }
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];
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];
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];
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 = [self hostName];
629   if (name == nil) name = @"*";
630   return [NSString stringWithFormat:@"%@:%i", name, [self port]];
631 }
632
633 - (NSString *)description {
634   NSString *name = [self hostName];
635
636   if (name == nil) name = @"*";
637   
638   return [NSString stringWithFormat:@"<InetSocketAddress: %@:%i>",
639                      name, [self port]];
640 }
641
642 @end /* NGInternetSocketAddress */
643
644 @implementation NGActiveSocket(NGInternetActiveSocket)
645
646 + (id)socketConnectedToPort:(int)_port onHost:(id)_host {
647   // this method calls +socketConnectedToAddress: with an 
648   // NGInternetSocketAddress
649   
650   return [self socketConnectedToAddress:
651                  [NGInternetSocketAddress addressWithPort:_port onHost:_host]];
652 }
653
654 - (BOOL)connectToPort:(int)_port onHost:(id)_host {
655   // this method calls -connectToAddress: with an NGInternetSocketAddress
656   
657   return [self connectToAddress:
658                  [NGInternetSocketAddress addressWithPort:_port onHost:_host]];
659 }
660
661 @end /* NGActiveSocket(NGInternetActiveSocket) */