diff --git a/cpukit/ChangeLog b/cpukit/ChangeLog index 2bd8ef4355..701540af6b 100644 --- a/cpukit/ChangeLog +++ b/cpukit/ChangeLog @@ -1,3 +1,17 @@ +2006-11-02 Steven Johnson + + * libnetworking/netdb.h, libnetworking/libc/gethostbyht.c, + libnetworking/libc/gethostnamadr.c: This patch adds a functional + gethostbyname_r to RTEMS. We were having problems with multiple + threads calling gethostbyname, so we decided the best way to deal + with it was to do it properly, rather than kludge up our code to make + gethostbyname safe. We have found several slightly different + parameter lists for this function, it does not seem to be standard. + The one we used has the linux interface. In RTEMS there was an + existing gethostbyname_r inside a #ifdef _THREAD_SAFE which was NOT + Threadsafe, as this just called gethostbyname. So we have placed all + of the additional code inside the #ifdef _THREAD_SAFE. + 2006-10-30 Joel Sherrill PR 841/rtems diff --git a/cpukit/libnetworking/libc/gethostbyht.c b/cpukit/libnetworking/libc/gethostbyht.c index 29f2805367..df785cc351 100644 --- a/cpukit/libnetworking/libc/gethostbyht.c +++ b/cpukit/libnetworking/libc/gethostbyht.c @@ -67,6 +67,7 @@ static char rcsid[] = "$Id$"; #include #include /* XXX */ #include /* XXX */ +#include #define MAXALIASES 35 @@ -78,6 +79,10 @@ static u_char host_addr[16]; /* IPv4 or IPv6 */ static char *h_addr_ptrs[2]; static int stayopen = 0; +static char* hostmap = NULL; +static unsigned int hostlen = 0; +static char *cur; + void _sethosthtent(f) int f; @@ -200,3 +205,115 @@ _gethostbyhtaddr(addr, len, af) endhostent(); return (p); } + + +#ifdef _THREAD_SAFE + +static int isblank ( int ch ) +{ + return ch == ' ' || ch == '\t'; +} + +struct hostent* gethostent_r(char* buf, int len) +{ + char *dest; + struct hostent* pe=(struct hostent*)buf; + char* last; + char* max=buf+len; + int aliasidx; + int curlen; + + + if (hostf<0) return 0; + fseek(hostf,0,SEEK_END); + curlen=ftell(hostf); + fseek(hostf,0,SEEK_SET); + + if (curlen > hostlen) { + if (hostmap) { + hostmap = realloc(hostmap,curlen); + } + else { + hostmap = malloc(curlen); + } + } + hostlen = curlen; + + if (hostmap) { + if (fread(hostmap,hostlen,1,hostf) != hostlen) { + hostmap=0; goto error; + } + cur=hostmap; + } + last=hostmap+hostlen; +again: + if ((size_t)lenh_name=0; + pe->h_aliases=(char**)dest; pe->h_aliases[0]=0; dest+=10*sizeof(char*); + pe->h_addr_list=(char**)dest; dest+=2*sizeof(char**); + if (cur>=last) return 0; + if (*cur=='#' || *cur=='\n') goto parseerror; + /* first, the ip number */ + pe->h_name=cur; + while (cur=last) return 0; + if (*cur=='\n') goto parseerror; + { + char save=*cur; + *cur=0; + pe->h_addr_list[0]=dest; + pe->h_addr_list[1]=0; + if (max-dest<16) goto nospace; + if (inet_pton(AF_INET6,pe->h_name,dest)>0) { + pe->h_addrtype=AF_INET6; + pe->h_length=16; + dest+=16; + } else if (inet_pton(AF_INET,pe->h_name,dest)>0) { + pe->h_addrtype=AF_INET; + pe->h_length=4; + dest+=4; + } else { + *cur=save; + goto parseerror; + } + *cur=save; + } + ++cur; + /* now the aliases */ + for (aliasidx=0;aliasidx<9;++aliasidx) { + while (curh_aliases[aliasidx]=cur; + while (curh_aliases[aliasidx]; + int len=cur-from; + if (max-desth_aliases[aliasidx]=dest; + memmove(dest,from,(size_t)(cur-from)); + dest+=len; + *dest=0; ++dest; + } + if (*cur=='\n') { ++cur; ++aliasidx; break; } + if (cur>=last || !isblank(*cur)) break; + cur++; + } + pe->h_aliases[aliasidx]=0; + pe->h_name=pe->h_aliases[0]; + pe->h_aliases++; + return pe; +parseerror: + while (cur0&&*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 (aftermax) 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 (buflenh_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 + diff --git a/cpukit/libnetworking/netdb.h b/cpukit/libnetworking/netdb.h index 29350e063b..3a12fda74d 100644 --- a/cpukit/libnetworking/netdb.h +++ b/cpukit/libnetworking/netdb.h @@ -160,6 +160,16 @@ void setnetent(int); void setprotoent(int); void setservent(int); +#ifdef _THREAD_SAFE +struct hostent* gethostent_r(char* buf, int len); +int gethostbyname_r(const char* name, + struct hostent* result, + char *buf, + int buflen, + struct hostent **RESULT, + int *h_errnop) ; +#endif + /* * PRIVATE functions specific to the FreeBSD implementation */