summaryrefslogblamecommitdiffstats
path: root/cpukit/libnetworking/libc/gethostnamadr.c
blob: 4dc18840e0a9c0550009852a1d2d6c7ab4e81a75 (plain) (tree)
























                                                                             


                   











































































                                                                                    
                                                          






















































                                                                                            











                                                              
                
                                                        

























                                                                













                                 















































































































































































































                                                                                                              
/*-
 * Copyright (c) 1994, Garrett Wollman
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <arpa/nameser.h>		/* XXX hack for _res */
#include <resolv.h>			/* XXX hack for _res */

#define _PATH_HOSTCONF	"/etc/host.conf"

enum service_type {
  SERVICE_NONE = 0,
  SERVICE_BIND,
  SERVICE_HOSTS,
  SERVICE_NIS };
#define SERVICE_MAX	SERVICE_NIS

static struct {
  const char *name;
  enum service_type type;
} service_names[] = {
  { "hosts", SERVICE_HOSTS },
  { "/etc/hosts", SERVICE_HOSTS },
  { "hosttable", SERVICE_HOSTS },
  { "htable", SERVICE_HOSTS },
  { "bind", SERVICE_BIND },
  { "dns", SERVICE_BIND },
  { "domain", SERVICE_BIND },
  { "yp", SERVICE_NIS },
  { "yellowpages", SERVICE_NIS },
  { "nis", SERVICE_NIS },
  { 0, SERVICE_NONE }
};

static enum service_type service_order[SERVICE_MAX + 1];
static int service_done = 0;

static enum service_type
get_service_name(const char *name) {
	int i;
	for(i = 0; service_names[i].type != SERVICE_NONE; i++) {
		if(!strcasecmp(name, service_names[i].name)) {
			return service_names[i].type;
		}
	}
	return SERVICE_NONE;
}

static void
init_services()
{
	char *cp, *p, buf[BUFSIZ];
	register int cc = 0;
	FILE *fd;

	if ((fd = (FILE *)fopen(_PATH_HOSTCONF, "r")) == NULL) {
				/* make some assumptions */
		service_order[0] = SERVICE_BIND;
		service_order[1] = SERVICE_HOSTS;
		service_order[2] = SERVICE_NONE;
	} else {
		while (fgets(buf, BUFSIZ, fd) != NULL && cc < SERVICE_MAX) {
			if(buf[0] == '#')
				continue;

			p = buf;
			while ((cp = strsep(&p, "\n \t,:;")) != NULL && *cp == '\0')
				;
			if (cp == NULL)
				continue;
			do {
				if (isalpha((int)cp[0])) {
					service_order[cc] = get_service_name(cp);
					if(service_order[cc] != SERVICE_NONE)
						cc++;
				}
				while ((cp = strsep(&p, "\n \t,:;")) != NULL && *cp == '\0')
					;
			} while(cp != NULL && cc < SERVICE_MAX);
		}
		service_order[cc] = SERVICE_NONE;
		fclose(fd);
	}
	service_done = 1;
}

struct hostent *
gethostbyname(const char *name)
{
	struct hostent *hp;

	if (_res.options & RES_USE_INET6) {		/* XXX */
		hp = gethostbyname2(name, AF_INET6);	/* XXX */
		if (hp)					/* XXX */
			return (hp);			/* XXX */
	}						/* XXX */
	return (gethostbyname2(name, AF_INET));
}

struct hostent *
gethostbyname2(const char *name, int type)
{
	struct hostent *hp = 0;
	int nserv = 0;

	if (!service_done)
		init_services();

	while (!hp) {
		switch (service_order[nserv]) {
		      case SERVICE_NONE:
			return NULL;
		      case SERVICE_HOSTS:
			hp = _gethostbyhtname(name, type);
			break;
		      case SERVICE_BIND:
			hp = _gethostbydnsname(name, type);
			break;
		      case SERVICE_NIS:
			hp = _gethostbynisname(name, type);
			break;
		}
		nserv++;
	}
	return hp;
}

int gethostbyaddr_r(const void *addr, socklen_t len, int type,
               struct hostent *ret, char *buf, size_t buflen,
               struct hostent **result, int *h_errnop)
{
  #warning "implement a proper gethostbyaddr_r"
 
  *result = gethostbyaddr( addr, len, type );
  if ( *result ) 
    return 0;
  return -1;
}

struct hostent *
gethostbyaddr(const void *addr, socklen_t len, int type)
{
	struct hostent *hp = 0;
	int nserv = 0;

	if (!service_done)
		init_services();

	while (!hp) {
		switch (service_order[nserv]) {
		      case SERVICE_NONE:
			return 0;
		      case SERVICE_HOSTS:
			hp = _gethostbyhtaddr(addr, len, type);
			break;
		      case SERVICE_BIND:
			hp = _gethostbydnsaddr(addr, len, type);
			break;
		      case SERVICE_NIS:
			hp = _gethostbynisaddr(addr, len, type);
			break;
		}
		nserv++;
	}
	return hp;
}

void
sethostent(stayopen)
	int stayopen;
{
	_sethosthtent(stayopen);
	_sethostdnsent(stayopen);
}

void
endhostent()
{
	_endhosthtent();
	_endhostdnsent();
}

#ifdef _THREAD_SAFE

/* return length of decoded data or -1 */
static int __dns_decodename(unsigned char *packet,unsigned int offset,unsigned char *dest,
         unsigned int maxlen,unsigned char* behindpacket) {
  unsigned char *tmp;
  unsigned char *max=dest+maxlen;
  unsigned char *after=packet+offset;
  int ok=0;
  for (tmp=after; maxlen>0&&*tmp; ) {
    if (tmp>=behindpacket) return -1;
    if ((*tmp>>6)==3) {   /* goofy DNS decompression */
      unsigned int ofs=((unsigned int)(*tmp&0x3f)<<8)|*(tmp+1);
      if (ofs>=(unsigned int)offset) return -1; /* RFC1035: "pointer to a _prior_ occurrance" */
      if (after<tmp+2) after=tmp+2;
      tmp=packet+ofs;
      ok=0;
    } else {
      unsigned int duh;
      if (dest+*tmp+1>max) return -1;
      if (tmp+*tmp+1>=behindpacket) return -1;
      for (duh=*tmp; duh>0; --duh)
  *dest++=*++tmp;
      *dest++='.'; ok=1;
      ++tmp;
      if (tmp>after) { after=tmp; if (!*tmp) ++after; }
    }
  }
  if (ok) --dest;
  *dest=0;
  return after-packet;
}

static int __dns_gethostbyx_r(const char* name, struct hostent* result,
      char *buf, size_t buflen,
      struct hostent **RESULT, int *h_errnop, int lookfor) {

  int names,ips;
  unsigned char *cur;
  unsigned char *max;
  unsigned char inpkg[1500];
  char* tmp;
  int size;

  if (lookfor==1) {
    result->h_addrtype=AF_INET;
    result->h_length=4;
  } else {
    result->h_addrtype=AF_INET6;
    result->h_length=16;
  }
  result->h_aliases=(char**)(buf+8*sizeof(char*));
  result->h_addr_list=(char**)buf;
  result->h_aliases[0]=0;

  cur=buf+16*sizeof(char*);
  max=buf+buflen;
  names=ips=0;

  if ((size=res_query(name,C_IN,lookfor,inpkg,512))<0) {
invalidpacket:
    *h_errnop=HOST_NOT_FOUND;
    return -1;
  }
  {
    tmp=inpkg+12;
    {
      char Name[257];
      unsigned short q=((unsigned short)inpkg[4]<<8)+inpkg[5];
      while (q>0) {
  if (tmp>(char*)inpkg+size) goto invalidpacket;
  while (*tmp) { tmp+=*tmp+1; if (tmp>(char*)inpkg+size) goto invalidpacket; }
  tmp+=5;
  --q;
      }
      if (tmp>(char*)inpkg+size) goto invalidpacket;
      q=((unsigned short)inpkg[6]<<8)+inpkg[7];
      if (q<1) goto nodata;
      while (q>0) {
  int decofs=__dns_decodename(inpkg,(size_t)(tmp-(char*)inpkg),Name,256,inpkg+size);
  if (decofs<0) break;
  tmp=inpkg+decofs;
  --q;
  if (tmp[0]!=0 || tmp[1]!=lookfor || /* TYPE != A */
      tmp[2]!=0 || tmp[3]!=1) {   /* CLASS != IN */
    if (tmp[1]==5) {  /* CNAME */
      tmp+=10;
      decofs=__dns_decodename(inpkg,(size_t)(tmp-(char*)inpkg),Name,256,inpkg+size);
      if (decofs<0) break;
      tmp=inpkg+decofs;
    } else
      break;
    continue;
  }
  tmp+=10;  /* skip type, class, TTL and length */
  {
    int slen;
    if (lookfor==1 || lookfor==28) /* A or AAAA*/ {
      slen=strlen(Name);
      if (cur+slen+8+(lookfor==28?12:0)>=max) { *h_errnop=NO_RECOVERY; return -1; }
    } else if (lookfor==12) /* PTR */ {
      decofs=__dns_decodename(inpkg,(size_t)(tmp-(char*)inpkg),Name,256,inpkg+size);
      if (decofs<0) break;
      tmp=inpkg+decofs;
      slen=strlen(Name);
    } else
      slen=strlen(Name);
    strcpy(cur,Name);
    if (names==0)
      result->h_name=cur;
    else
      result->h_aliases[names-1]=cur;
    result->h_aliases[names]=0;
    if (names<8) ++names;
/*    cur+=slen+1; */
    cur+=(slen|3)+1;
    result->h_addr_list[ips++] = cur;
    if (lookfor==1) /* A */ {
      *(int*)cur=*(int*)tmp;
      cur+=4;
      result->h_addr_list[ips]=0;
    } else if (lookfor==28) /* AAAA */ {
      {
        int k;
        for (k=0; k<16; ++k) cur[k]=tmp[k];
      }
      cur+=16;
      result->h_addr_list[ips]=0;
    }
  }
/*        puts(Name); */
      }
    }
  }
  if (!names) {
nodata:
    *h_errnop=NO_DATA;
    return -1;
  }
  *h_errnop=0;
  *RESULT=result;
  return 0;
}




int gethostbyname_r(const char*      name, 
        struct hostent*  result,
        char            *buf, 
        int              buflen,
        struct hostent **RESULT, 
        int             *h_errnop) 
{
        
  size_t L=strlen(name);
  result->h_name=buf;
  if (buflen<L) { *h_errnop=ERANGE; return 1; }
  strcpy(buf,name);

  result->h_addr_list=(char**)(buf+strlen(name)+1);
  result->h_addr_list+=sizeof(unsigned long)-((unsigned long)(result->h_addr_list)&(sizeof(unsigned long)-1));
  result->h_addr_list[0]=(char*)&result->h_addr_list[2];
  if (inet_pton(AF_INET,name,result->h_addr_list[0])) {
    result->h_addrtype=AF_INET;
    result->h_length=4;
commonip:
    result->h_aliases=result->h_addr_list+2*sizeof(char**);
    result->h_aliases[0]=0;
    result->h_addr_list[1]=0;
    *RESULT=result;
    *h_errnop=0;
    return 0;
  } else if (inet_pton(AF_INET6,name,result->h_addr_list[0])) {
    result->h_addrtype=AF_INET6;
    result->h_length=16;
    goto commonip;
  }


  {
    struct hostent* r;
    sethostent(0);
    while ((r=gethostent_r(buf,buflen))) {
      int i;
      if (r->h_addrtype==AF_INET && !strcasecmp(r->h_name,name)) {  /* found it! */
found:
  memmove(result,r,sizeof(struct hostent));
  *RESULT=result;
  *h_errnop=0;
  endhostent();
  return 0;
      }
      for (i=0; i<16; ++i) {
  if (r->h_aliases[i]) {
    if (!strcasecmp(r->h_aliases[i],name)) goto found;
  } else break;
      }
    }
    endhostent();
  }

  return __dns_gethostbyx_r(name,result,buf+L,buflen-L,RESULT,h_errnop,1);
}

#endif