1 /*
2  * BSD-style socket emulation library for the Mac
3  * Original author: Tom Milligan
4  * Current author: Charlie Reiman - creiman@ncsa.uiuc.edu
5  *
6  * This source file is placed in the public domian.
7  * Any resemblance to NCSA Telnet, living or dead, is purely coincidental.
8  *
9  *      National Center for Supercomputing Applications
10  *      152 Computing Applications Building
11  *      605 E. Springfield Ave.
12  *      Champaign, IL  61820
13  *
14  *
15  * RCS Modification History:
16  * $Log: OTnetdb.c,v $
17  * Revision 1.4  2006/11/27 20:21:26  kans
18  * replaced netdb.h with carbon_netdb.h
19  *
20  * Revision 1.3  2001/11/07 22:45:02  juran
21  * Phil Churchill's 2001-05-07 changes.
22  * Remove debugging.
23  * Use Idle().
24  *
25  * Revision 1.2  ??  juran
26  * Minor edits.
27  *
28  * Revision 1.1  2001/04/03 20:35:27  juran
29  * Phil Churchill's MIT-derived OT sockets library.  No changes prior to initial check-in.
30  *
31  * Revision 6.1  2000/03/20 21:50:35  kans
32  * initial checkin - initial work on OpenTransport (Churchill)
33  *
34  * Revision 6.1  1999/11/17 20:52:50  kans
35  * changes to allow compilation under c++
36  *
37  * Revision 6.0  1997/08/25 18:37:31  madden
38  * Revision changed to 6.0
39  *
40  * Revision 4.1  1995/08/01 16:17:20  kans
41  * New gethostbyname and gethostbyaddr contributed by Doug Corarito
42  *
43  * Revision 4.0  1995/07/26  13:56:09  ostell
44  * force revision to 4.0
45  *
46  * Revision 1.3  1995/05/17  17:56:47  epstein
47  * add RCS log revision history
48  *
49  */
50 
51 /*
52  *	Internet name routines that every good unix program uses...
53  *
54  *		gethostbyname
55  *		gethostbyaddr
56  *      gethostid
57  *		gethostname
58  *		getdomainname
59  *		inet_ntoa
60  *		inet_addr
61  */
62 
63 #include <stdio.h>
64 #include <stdlib.h>				// malloc
65 #include <string.h>
66 
67 #include <MacTypes.h>
68 #include <Resources.h>
69 #include <Errors.h>
70 #include <OSUtils.h>
71 #include <OpenTransport.h>
72 #include <OpenTptInternet.h>	// for TCP/IP
73 
74 #include <carbon_netdb.h>
75 #include <neterrno.h>
76 #include "SocketsInternal.h"
77 #include "a_inet.h"
78 
79 /* global variables...
80  */
81 static int h_errno;
82 static short driver = 0;
83 static Boolean gOTInited = false;
84 EndpointRef gDNRep = kOTInvalidEndpointRef;
85 OTNotifyUPP gDNRYieldNotifyUPP = NULL;
86 
87 
88 /* DNR notifier process
89  *
90  *	This function receives any notifications from the OT DNR routines.
91  *
92  *	Typically this is used to permit other threads to run while a time
93  *	consuming process runs, but it can be used to notify of certain failures.
94  *
95  *	You have the ability to handle user abort requests via the Idle() function...
96  */
97 
DNRYieldNotifier(void * contextPtr,OTEventCode code,OTResult result,void * cookie)98 static pascal void DNRYieldNotifier( void* contextPtr, OTEventCode code,
99                              OTResult result, void* cookie)
100 {
101 #pragma unused(contextPtr)
102 #pragma unused(result)
103 #pragma unused(cookie)
104 	OSStatus status;
105 
106 	switch (code) {
107 	  case kOTSyncIdleEvent:
108 		status = Idle();
109 		break;
110 	  case kOTProviderWillClose:			// if the dnr service is going away
111 	  case kOTProviderIsClosed:				// or already gone...
112 		(void) OTCloseProvider(gDNRep);		// then close our provider
113 		gDNRep = kOTInvalidProviderRef;		// and note that.
114 		break;
115 	  default:
116 		/* do nothing */
117 		break;
118 	}
119 }
120 
121 
122 /* ot_OpenDriver
123  *
124  *	Performs required initializations needed before OT can be used
125  */
126 
ot_OpenDriver(void)127 OSStatus ot_OpenDriver( void)
128 {
129 	OSStatus err;
130 	int i;
131 
132 	// if this function has already been performed, then just return
133 	if( gOTInited){
134 		return( kOTNoError);
135 	}
136 
137 	// Set up the global array of socket pointers and clear it
138 	if( gSockets == NULL)
139 		gSockets = (SocketPtr *) malloc(kNumSockets * sizeof(SocketPtr));
140 
141 	if( gSockets == NULL)
142 		return(memFullErr);
143 
144 	for( i = 0; i < kNumSockets; i++)
145 		gSockets[i] = NULL;
146 
147 #if TARGET_API_MAC_CARBON
148 	err = InitOpenTransportInContext(kInitOTForApplicationMask, NULL);
149 #else
150 	err = InitOpenTransport();
151 #endif
152 
153 	gOTInited = (kOTNoError == err);
154 
155 	return( err);
156 }
157 
158 
159 /* ot_DNRInit
160  *
161  *	Function which initializes the domain name resolver for OpenTransport
162  *	for use by gethostbyname, gethostbyaddress, and other utils
163  */
164 
ot_DNRInit(void)165 OSStatus ot_DNRInit(void)
166 {
167 	OSStatus theError = kOTNoError;
168 
169 	// First, check to see if we've already initialized.  If so, then just return...
170 	if( gDNRep != kOTInvalidProviderRef){
171 		return (kOTNoError);
172 	}
173 
174 	// Next, check to see if any OT service has been used yet
175 	if (!gOTInited){
176 		theError = ot_OpenDriver();
177 	}
178 
179 	// Get the internet services reference for DNS
180 	if( theError == kOTNoError){
181 #if TARGET_API_MAC_CARBON
182 		gDNRep = OTOpenInternetServicesInContext( kDefaultInternetServicesPath, 0, &theError, NULL);
183 #else
184 		gDNRep = OTOpenInternetServices( kDefaultInternetServicesPath, 0, &theError);
185 #endif
186 	}
187 
188 	/* We have to choose to be synchronous or async, sync is simpler but
189 	 *	I think that async could be done in the future
190 	 */
191 	if( theError == kOTNoError){
192 		theError = OTSetSynchronous( gDNRep);
193 	}
194 
195 	// Give OT a pointer to a notifier function that handles async events
196 	if( theError == kOTNoError){
197 		gDNRYieldNotifyUPP = NewOTNotifyUPP(DNRYieldNotifier);
198 		theError = OTInstallNotifier(gDNRep, gDNRYieldNotifyUPP, nil);
199 	}
200 
201 	/* If we get idle events, then implementing a multi-threaded app shouldn't
202 	 *	be any big deal
203 	 */
204 	if( theError == kOTNoError){
205 		theError = OTUseSyncIdleEvents(gDNRep, true);
206 	}
207 	return( theError);
208 }
209 
210 
211 /* ot_DNRClose
212  *
213  *	If OT ever needs any formal closing/cleanup actions, this is the place
214  *	to put it...
215  */
216 
ot_DNRClose(InetSvcRef theRef)217 OSStatus ot_DNRClose( InetSvcRef theRef)
218 {
219 	OSStatus theErr = kOTNoError;
220 
221 	return theErr;
222 }
223 
224 
225 /*
226  *   Gethostbyname and gethostbyaddr each return a pointer to an
227  *   object with the following structure describing an Internet
228  *   host referenced by name or by address, respectively. This
229  *   structure contains the information obtained from the OpenTransport
230  *   name server.
231  *
232  *   struct    hostent
233  *   {
234  *        char *h_name;
235  *        char **h_aliases;
236  *        int  h_addrtype;
237  *        int  h_length;
238  *        char **h_addr_list;
239  *   };
240  *   #define   h_addr  h_addr_list[0]
241  *
242  *   The members of this structure are:
243  *
244  *   h_name       Official name of the host.
245  *
246  *   h_aliases    A zero terminated array of alternate names for the host.
247  *
248  *   h_addrtype   The type of address being  returned; always AF_INET.
249  *
250  *   h_length     The length, in bytes, of the address.
251  *
252  *   h_addr_list  A zero terminated array of network addresses for the host.
253  *
254  *   Error return status from gethostbyname and gethostbyaddr  is
255  *   indicated by return of a null pointer.  The external integer
256  *   h_errno may then  be checked  to  see  whether  this  is  a
257  *   temporary  failure  or  an  invalid  or  unknown  host.  The
258  *   routine herror  can  be  used  to  print  an error  message
259  *   describing the failure.  If its argument string is non-NULL,
260  *   it is printed, followed by a colon and a space.   The  error
261  *   message is printed with a trailing newline.
262  *
263  *   h_errno can have the following values:
264  *
265  *     HOST_NOT_FOUND  No such host is known.
266  *
267  *     TRY_AGAIN	This is usually a temporary error and
268  *					means   that  the  local  server  did  not
269  *					receive a response from  an  authoritative
270  *					server.   A  retry at some later time may
271  *					succeed.
272  *
273  *     NO_RECOVERY	Some unexpected server failure was encountered.
274  *	 				This is a non-recoverable error.
275  *
276  *     NO_DATA		The requested name is valid but  does  not
277  *					have   an IP  address;  this  is not  a
278  *					temporary error. This means that the  name
279  *					is known  to the name server but there is
280  *					no address  associated  with  this  name.
281  *					Another type of request to the name server
282  *					using this domain name will result in  an
283  *					answer;  for example, a mail-forwarder may
284  *					be registered for this domain.
285  *					(NOT GENERATED BY THIS IMPLEMENTATION)
286  */
287 
288 static char	cname[255];
289 static struct InetHostInfo hinfo;
290 
291 #define MAXALIASES 2
292 static char	aliases[MAXALIASES+1][kMaxHostNameLen + 1];
293 static char* aliasPtrs[MAXALIASES+1] = {NULL};
294 static InetHost* addrPtrs[kMaxHostAddrs+1] = {0};
295 
296 
297 static struct hostent unixHost =
298 {
299 	cname,
300 	aliasPtrs,
301 	AF_INET,
302 	sizeof(UInt32),
303 	(char **) addrPtrs
304 };
305 
306 
307 /* Gethostbyname
308  *
309  *	UNIX function implemented on top of OpenTransport
310  */
311 
312 struct hostent *
gethostbyname(const char * name)313 gethostbyname(const char *name)
314 {
315 	extern EndpointRef gDNRep;
316 	OSStatus	theErr;
317 	int i, l;
318 
319 	// open or get the current resolver reference
320 	if( gDNRep == kOTInvalidEndpointRef){
321 		theErr = ot_DNRInit();
322 		if( theErr != kOTNoError){
323 			ncbi_SetOTErrno( theErr);
324 			return (NULL);
325 		}
326 	}
327 
328 	// Call OT to resolve the address...
329 	theErr = OTInetStringToAddress( gDNRep, (char*) name, &hinfo);
330 
331 	if( theErr != kOTNoError){
332 		ncbi_SetOTErrno( theErr);
333 		return( NULL);
334 	}
335 
336 	// sometimes the hostname returned has a trailing dot?? (nuke it)
337 	l = strlen( hinfo.name);
338 	if( hinfo.name[l-1] == '.'){
339 		hinfo.name[l-1] = '\0';
340 	}
341 
342 #if NETDB_DEBUG >= 5
343 	printf("gethostbyname: name '%s' returned the following addresses:\n", name);
344 	for( i = 0; i < kMaxHostAddrs; i++){
345 		if( hinfo.addrs[i] == 0) break;
346 		printf("%08x\n", hinfo.addrs[i]);
347 	}
348 #endif
349 
350 	// copy the official hostname to the return struct and first alias
351 	strcpy( unixHost.h_name, hinfo.name);
352 
353 	// copy the name passed in as the first alias
354 	strncpy( aliases[0], name, kMaxHostNameLen);
355 	aliases[0][kMaxHostNameLen] = '\0';
356 
357 	// if the answer is different from the query, copy the query as another alias
358 	theErr = strcmp( name, hinfo.name);
359 	if( theErr != 0){
360 		strncpy( aliases[1], hinfo.name, kMaxHostNameLen);
361 		aliases[1][kMaxHostNameLen] = '\0';
362 	}
363 
364 	// This block will not need to be changed if we find a way to determine
365 	// more aliases than are currently implemented
366 	for( i = 0; i <= MAXALIASES; i++){
367 		if( aliases[i][0] != '\0'){
368 			aliasPtrs[i] = &aliases[i][0];
369 		} else{
370 			aliasPtrs[i] = NULL;
371 		}
372 	}
373 
374 	// copy all of the returned addresses
375 	for( i = 0; i < kMaxHostAddrs && hinfo.addrs[i] != 0; i++){
376 		addrPtrs[i] = &hinfo.addrs[i];
377 	}
378 
379 	// make the rest NULL
380 	for( ; i < kMaxHostAddrs; i++){
381 		addrPtrs[i] = NULL;
382 	}
383 	return( &unixHost);
384 }
385 
386 
387 /* gethostbyaddr
388  *
389  *  Currently only supports IPv4
390  */
391 
392 struct hostent *
gethostbyaddr(InetHost * addrP,int len,int type)393 gethostbyaddr( InetHost *addrP, int len, int type)
394 {
395 	extern EndpointRef gDNRep;
396 	OSStatus	theErr = noErr;
397 	char	addrString[255];
398 	int		l;
399 
400 	// Check that the arguments are appropriate
401 	if( len != INADDRSZ || (type != AF_INET /* && type != AF_UNSPEC */)){
402 		ncbi_SetErrno( EAFNOSUPPORT);
403 		return( NULL);
404 	}
405 
406 	// open or get the current resolver reference
407 	if( gDNRep == kOTInvalidEndpointRef){
408 		theErr = ot_DNRInit();
409 		if( theErr != kOTNoError){
410 			ncbi_SetErrno( theErr);
411 			return (NULL);
412 		}
413 	}
414 
415 #if NETDB_DEBUG >= 5
416 	printf("gethostbyaddr: Looking up hostname for address 0x%8x\n", *addrP);
417 #endif
418 
419 	/*  OpenTransport call to get the address as a string */
420 	theErr = OTInetAddressToName( gDNRep, *(InetHost*)addrP, addrString);
421 	if( theErr == noErr){
422 		// for some reason the names have a trailing "."???
423 		// Next couple of lines remove it
424 		l = strlen( addrString);
425 		if( addrString[l-1] == '.'){
426 			addrString[l-1] = '\0';
427 		}
428 		// with the full name, use gethostbyname to fill in all the blanks...
429 		return (gethostbyname(addrString) );
430 	}
431 	else{
432 		ncbi_SetErrno( theErr);
433 	}
434 	return( NULL);
435 }
436 
437 /*
438  * gethostid()
439  *
440  * Get Internet address from the primary enet interface.
441  */
442 
gethostid(void)443 unsigned long gethostid( void)
444 {
445 	OSStatus          theErr = kOTNoError;
446 	InetInterfaceInfo info;
447 	extern EndpointRef gDNRep;
448 
449 	// open or get the current resolver reference
450 	if( gDNRep == kOTInvalidEndpointRef){
451 		theErr = ot_DNRInit();
452 		if( theErr != kOTNoError){
453 			ncbi_SetErrno( theErr);
454 			return 0;
455 		}
456 	}
457 	theErr = OTInetGetInterfaceInfo( &info, kDefaultInetInterface);
458 	if(theErr != kOTNoError){
459 		ncbi_SetErrno( theErr);
460 		return 0;
461 	}
462 	return info.fAddress;
463 }
464 
465 
466 /*
467  * gethostname()
468  *
469  *	Try to get my host name from DNR. If it fails, just return my
470  *	IP address as ASCII. This is non-standard, but since Mac's don't require
471  *	hostnames to run, we don't have many other options.
472  */
473 
gethostname(char * machname,size_t buflen)474 int gethostname( char* machname, size_t buflen)
475 {
476 	OSStatus          theErr = kOTNoError;
477 	InetHost		  addr;
478 	struct hostent   *host;
479 	char* firstDot;
480 
481 	if( (machname == NULL) || (buflen < 31)){
482 		ncbi_SetErrno( EINVAL);
483 		return( -1);
484 	}
485 
486 	addr = gethostid();
487 
488 	host = gethostbyaddr( &addr, INADDRSZ, AF_INET);
489 	if(host == NULL){
490 		ncbi_SetErrno( EINVAL);
491 		return( -1);
492 	}
493 
494 	firstDot = strchr(host->h_name, '.');
495 	if(firstDot != NULL && *firstDot == '.'){
496 		*firstDot = '\0';
497 	}
498 
499 	strncpy( machname, host->h_name, buflen);
500 	machname[buflen-1] = '\0';  // strncpy doesn't always null terminate
501 
502 	if(theErr != kOTNoError)
503 		return(-1);
504 	else
505 		return(0);
506 }
507 
508 /* inet_ntoa()
509  *
510  *	Converts an IP address in 32 bit IPv4 address to a dotted string notation
511  *  Function returns a pointer to the resulting string (success), or NULL (failure).
512  */
513 
inet_ntoa(struct in_addr addr)514 char *inet_ntoa( struct in_addr addr)
515 {
516 	static char addrString[INET_ADDRSTRLEN + 1 /* '\0' */];
517 
518 	OTInetHostToString( addr.s_addr, addrString);
519 
520 	return (addrString);
521 }
522 
523 /*
524  * inet_aton() - converts an IP address in dotted string notation to a 32 bit IPv4 address
525  *               Function returns 1 on success and 0 on failure.
526  */
inet_aton(const char * str,struct in_addr * addr)527 int inet_aton(const char *str, struct in_addr *addr)
528 {
529 	InetHost host;
530 	OSStatus theError;
531 
532 	if((str == NULL) || (addr == NULL)){
533 		return( INET_FAILURE);
534 	}
535 
536 	theError = OTInetStringToHost((char *) str, &host);
537 	if(theError != kOTNoError){
538 		ncbi_SetErrno( theError);
539 		return( INET_FAILURE);
540 	}
541 	addr->s_addr = host;
542 	return(INET_SUCCESS);
543 }
544 
545 
546 /* inet_addr
547  *
548  *	Does much the same thing as inet_aton, but returns the value rather than
549  *	modifying a parameter that's passed in.
550  *		Note: failure is reported by returning INADDR_NONE (255.255.255.255)
551  */
552 
inet_addr(const char * str)553 InetHost inet_addr(const char *str)
554 {
555 	OSStatus       theError = kOTNoError;
556 	struct in_addr addr;
557 
558 	theError = inet_aton(str, &addr);
559 
560 	if(theError != INET_SUCCESS){
561 		addr.s_addr = INADDR_NONE;
562 	}
563 
564 	return(addr.s_addr);
565 }
566 
567 
568 /*		One last unix compatibility function that was included
569  *		in the ncsa socket lib...
570  */
bzero(char * b,long s)571 void bzero( char *b, long s)
572 {
573 	for( ; s ; ++b, --s)
574 		*b = 0;
575 }
576 
577 
578 /*		Error reporting strings and functions
579  */
580 
581 char* h_errlist[] = {
582 	"Error 0",
583 	"Unknown host",						/* 1 HOST_NOT_FOUND */
584 	"Host name lookup failure",			/* 2 TRY_AGAIN */
585 	"Unknown server error",				/* 3 NO_RECOVERY */
586 	"No address associated with name",	/* 4 NO_ADDRESS */
587 };
588 
589 // compute the number of elements in the list (one less place for human error)
590 const int	h_nerr = { sizeof(h_errlist)/sizeof(h_errlist[0]) };
591 
herror(char * s)592 void herror(char *s)
593 {
594 	fprintf(stderr,"%s: ",s);
595 	if (h_errno < h_nerr)
596 		fprintf(stderr,h_errlist[h_errno]);
597 	else
598 		fprintf(stderr,"error %d",h_errno);
599 	fprintf(stderr,"\n");
600 }
601 
herror_str(int theErr)602 char *herror_str(int theErr)
603 {
604 	if (theErr > h_nerr )
605 		return NULL;
606 	else
607 		return h_errlist[theErr];
608 }
609 
610 // end of file
611