/*************************************************************************** CDnsClient.c Network component (c) 2003-2004 Daniel Campos Fernández This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ***************************************************************************/ #define __CDNSCLIENT_C #include #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "CDnsClient.h" #ifndef _REENTRANT #define _REENTRANT #endif void **dns_object=NULL; int dns_count=0; sem_t dns_th_pipe; int dns_r_pipe=-1; int dns_w_pipe=-1; DECLARE_EVENT (Finished); /********************************************************** DnsClient pipe message protocol: 1) (void*)DNSCLIENT* 2) Action : '0' --> dns_get_name, '1' --> dns_get_ip 3) Result : string finished with '\x10' *********************************************************/ void dns_callback(long lParam) { /*********************************************************** This function reads a message sent by a thread, and then raises "Finished" event if necessary, and fills HostName and HostIP properties. This function will run on the main thread. ************************************************************/ CDNSCLIENT *mythis; void *v_obj; char Action[1]; char BufRead[1]; char *Buf=NULL; int Position; int myloop; int test_id; struct pollfd mypoll; int idata=1; if (dns_r_pipe==-1) return; sem_wait(&dns_th_pipe); while (1) { Position=0; BufRead[0]='\0'; mypoll.fd=dns_r_pipe; mypoll.events=POLLIN; mypoll.revents=0; idata=poll(&mypoll,1,0); if (!idata) break; read (dns_r_pipe,&v_obj,sizeof(void*)); read (dns_r_pipe,&test_id,sizeof(int)); read (dns_r_pipe,Action,sizeof(char)); GB.Alloc(POINTER(&Buf),sizeof(char)); while (BufRead[0] != '\x10') { read(dns_r_pipe,BufRead,sizeof(char)); if (BufRead[0]!='\x10') { Buf[Position]=BufRead[0]; Position++; GB.Realloc(POINTER(&Buf),(Position+1)*sizeof(char)); } else Buf[Position]='\0'; } Position=-1; for (myloop=0;myloop=0) { mythis=(CDNSCLIENT*)v_obj; if (mythis->iStatus && (mythis->i_id==test_id)) { if (Action[0]=='1') { GB.FreeString(&mythis->sHostIP); GB.NewString(&mythis->sHostIP, Buf ,0); } else { GB.FreeString(&mythis->sHostName); GB.NewString(&mythis->sHostName,Buf,0); } mythis->iStatus=0; if (mythis->finished_callback) { GB.Ref(mythis); GB.Post(mythis->finished_callback, (long)mythis->CliParent); } else { GB.Ref(mythis); GB.Post((void (*)())dns_event, (long)mythis); } } } GB.Free(POINTER(&Buf)); } sem_post(&dns_th_pipe); } /************************************************** To raise an event **************************************************/ void dns_event(CDNSCLIENT *mythis) { GB.Raise(mythis,Finished,0); GB.Unref(POINTER(&mythis)); } void* dns_get_name(void* v_obj) { /**************************************************************** This function will run in a thread different from main thread, and when it finish its proccess, it sends a message to the main thread using a pipe *****************************************************************/ char Buf[1]; int myid; int res; CDNSCLIENT *mythis; char host[1024]; struct sockaddr_in sa; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); Buf[0]='0'; mythis=(CDNSCLIENT*)v_obj; sem_wait(&mythis->sem_id); myid=mythis->i_id; sem_post(&mythis->sem_id); ((struct sockaddr*)&sa)->sa_family=AF_INET; bzero(host,1024); sa.sin_port=0; inet_aton(mythis->sHostIP, &sa.sin_addr); res=getnameinfo((struct sockaddr*)&sa,sizeof(struct sockaddr_in),host,1024*sizeof(char),NULL,0,NI_NAMEREQD); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); sem_wait(&dns_th_pipe); write (dns_w_pipe,&v_obj,sizeof(void*)); /* object */ write (dns_w_pipe,&myid,sizeof(int)); /* id */ write (dns_w_pipe,Buf,sizeof(char)); /* Action */ if (!res) write (dns_w_pipe,host,strlen(host)*sizeof(char)); write (dns_w_pipe,"\x10",sizeof(char)); sem_post(&dns_th_pipe); return NULL; } /**************************************************************** This function will run in a thread different from main thread, and when it finish its proccess, it sends a message to the main thread using a pipe *****************************************************************/ void* dns_get_ip(void* v_obj) { char Buf[1]; char *BufData; int myid; struct addrinfo *stHost; struct sockaddr_in *addr; CDNSCLIENT *mythis; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); Buf[0]='1'; mythis=(CDNSCLIENT*)v_obj; sem_wait(&mythis->sem_id); myid=mythis->i_id; sem_post(&mythis->sem_id); if (getaddrinfo(mythis->sHostName,NULL,NULL,&stHost)) stHost=NULL; if (stHost) if ( stHost[0].ai_family!=PF_INET ) stHost=NULL; sem_wait(&dns_th_pipe); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); write (dns_w_pipe,&v_obj,sizeof(void*)); /* object */ write (dns_w_pipe,&myid,sizeof(int)); /* id */ write (dns_w_pipe,Buf,sizeof(char)); /* action */ if (stHost!=NULL) { addr=(struct sockaddr_in*)stHost[0].ai_addr; BufData=inet_ntoa(addr->sin_addr); if (BufData) write (dns_w_pipe,BufData,strlen(BufData)*sizeof(char)); } write (dns_w_pipe,"\x10",sizeof(char)); sem_post(&dns_th_pipe); return NULL; } void dns_close_all(CDNSCLIENT *mythis) { if (mythis->iStatus) { pthread_cancel(mythis->th_id); /* cancel thread */ pthread_join(mythis->th_id,NULL); sem_destroy(&mythis->sem_id); mythis->iStatus=0; /* inactive */ dns_callback(dns_r_pipe); /* freeing pipe data */ } } int dns_thread_getname(CDNSCLIENT *mythis) { sem_wait(&mythis->sem_id); mythis->i_id++; sem_post(&mythis->sem_id); mythis->iStatus=1; if (pthread_create(&mythis->th_id,NULL,dns_get_name,(void*)mythis) ) { mythis->iStatus=0; return 1; } pthread_detach(mythis->th_id); return 0; } int dns_thread_getip(CDNSCLIENT *mythis) { sem_wait(&mythis->sem_id); mythis->i_id++; sem_post(&mythis->sem_id); mythis->iStatus=1; if (pthread_create(&mythis->th_id,NULL,dns_get_ip,(void*)mythis) ) { mythis->iStatus=0; return 1; } pthread_detach(mythis->th_id); return 0; } int dns_set_async_mode(int myval,CDNSCLIENT *mythis) { int dpipe[2]; if (myval && (dns_r_pipe==-1)) { if (pipe(dpipe)) return 1; dns_r_pipe=dpipe[0]; dns_w_pipe=dpipe[1]; sem_init(&dns_th_pipe,0,1); GB.Watch(dns_r_pipe , GB_WATCH_READ , (void *)dns_callback,0); } mythis->iAsync=myval; return 0; } /************************************************************************************* ###################################################################################### --------------------- DNSCLIENT GAMBAS INTERFACE IMPLEMENTATION ----------------- ###################################################################################### **************************************************************************************/ /********************************************* This property gets/sets the name of a Host *********************************************/ BEGIN_PROPERTY ( HostName ) if (READ_PROPERTY) { if (THIS->iStatus){ GB.ReturnString(NULL); return; } GB.ReturnString(THIS->sHostName); return; } if (THIS->iStatus) { GB.Error("HostIP can not be changed while working"); return;} GB.FreeString(&THIS->sHostName); GB.StoreString(PROP(GB_STRING), &THIS->sHostName); END_PROPERTY /************************************************* This property gets/sets the IP address of a Host *************************************************/ BEGIN_PROPERTY ( HostIP ) if (READ_PROPERTY) { if (THIS->iStatus) { GB.ReturnString(NULL); return; } GB.ReturnString(THIS->sHostIP); return; } if (THIS->iStatus) { GB.Error("HostIP can not be changed while working"); return; } GB.FreeString(&THIS->sHostIP); GB.StoreString(PROP(GB_STRING), &THIS->sHostIP); END_PROPERTY /************************************************* This property gets/sets asynchronous state: FALSE : Synchronous TRUE : Asynchronous *************************************************/ BEGIN_PROPERTY ( CDNSCLIENT_Async ) if (READ_PROPERTY) { GB.ReturnBoolean(THIS->iAsync); return; } if (THIS->iStatus) { GB.Error("Async mode can not be changed while working"); return; } if ( !dns_set_async_mode (VPROP(GB_BOOLEAN),THIS)) return; GB.Error("No resources available start asynchronous mode"); END_PROPERTY /********************************************************** This property gets status : 0 --> Inactive, 1 --> Working **********************************************************/ BEGIN_PROPERTY ( CDNSCLIENT_Status ) GB.ReturnInteger(THIS->iStatus); END_PROPERTY /************************************************* Gambas object "Constructor" *************************************************/ BEGIN_METHOD_VOID(CDNSCLIENT_new) THIS->CliParent=NULL; THIS->finished_callback=NULL; THIS->sHostIP=NULL; THIS->sHostName=NULL; THIS->iStatus=0; THIS->iAsync=0; THIS->i_id=0; sem_init(&THIS->sem_id,0,1); dns_count++; if (dns_object==NULL) GB.Alloc(POINTER(&dns_object),sizeof(void*)); else GB.Realloc(POINTER(&dns_object),dns_count*sizeof(void*)); dns_object[dns_count-1]=(void*)THIS; END_METHOD /************************************************* Gambas object "Destructor" *************************************************/ BEGIN_METHOD_VOID(CDNSCLIENT_free) int myloop; int Position=-1; dns_close_all(THIS); GB.FreeString(&THIS->sHostIP); GB.FreeString(&THIS->sHostName); for (myloop=0;myloop=0) { for (myloop=Position;myloop< (dns_count-1);myloop++) { dns_object[myloop]=dns_object[myloop+1]; } dns_count--; if (!dns_count) { GB.Free(POINTER(&dns_object)); if (dns_r_pipe != -1) { GB.Watch(dns_r_pipe , GB_WATCH_NONE , (void *)dns_callback,0); close(dns_r_pipe); close(dns_w_pipe); dns_r_pipe=-1; dns_w_pipe=-1; } } } END_METHOD /************************************************* To cancel an asynchronous operation *************************************************/ BEGIN_METHOD_VOID(CDNSCLIENT_Stop) dns_close_all(THIS); END_METHOD /***************************************************************************** This method takes the Host IP, which was stored using HostName property, and trasnlates it to IP address *****************************************************************************/ BEGIN_METHOD_VOID(CDNSCLIENT_GetHostName) struct hostent *stHost=NULL; struct in_addr addr; if (THIS->iStatus) { GB.Error("Object is already working"); return; } if (THIS->sHostIP) { if ( THIS->iAsync) { /* Asynchronous mode */ sem_wait(&THIS->sem_id); THIS->i_id++; sem_post(&THIS->sem_id); THIS->iStatus=1; if (dns_thread_getname(THIS)) { GB.Error("No resources available to create a thread"); return; } } else { /* Synchronous mode */ inet_aton(THIS->sHostIP,&addr); #ifdef __sun__ stHost=gethostbyaddr((const char *)&addr,sizeof(addr),AF_INET); #else stHost=gethostbyaddr(&addr,sizeof(addr),AF_INET); #endif if (stHost==NULL) { GB.FreeString(&THIS->sHostName); } else { GB.FreeString(&THIS->sHostName); GB.NewString(&THIS->sHostName,stHost->h_name,0); } GB.Raise((void*)THIS,Finished,0); } } else { GB.FreeString(&THIS->sHostName); } END_METHOD /***************************************************************************** This method takes the Host IP, which was stored using HostIP property, and trasnlates it to host name *****************************************************************************/ BEGIN_METHOD_VOID(CDNSCLIENT_GetHostIP) struct hostent *stHost=NULL; if (THIS->iStatus) { GB.Error("Object is already working"); return; } if (THIS->sHostName) { if ( THIS->iAsync ) { /* Asynchronous mode */ sem_wait(&THIS->sem_id); THIS->i_id++; sem_post(&THIS->sem_id); THIS->iStatus=1; if (dns_thread_getip(THIS)) { GB.Error("No resources available to create a thread"); return; } } else { /* Synchronous mode */ stHost=gethostbyname(THIS->sHostName); if (stHost==NULL) { GB.FreeString(&THIS->sHostIP); } else { GB.FreeString(&THIS->sHostIP); GB.NewString(&THIS->sHostIP, inet_ntoa(*( (struct in_addr*)stHost->h_addr ) ) ,0); } GB.Raise((void*)THIS,Finished,0); } } else { GB.FreeString(&THIS->sHostIP); } END_METHOD /*************************************************************** Here we declare the public interface of DnsClient class ***************************************************************/ GB_DESC CDnsClientDesc[] = { GB_DECLARE("DnsClient", sizeof(CDNSCLIENT)), GB_EVENT("Finished", NULL, NULL, &Finished), GB_METHOD("_new", NULL, CDNSCLIENT_new, NULL), GB_METHOD("_free", NULL, CDNSCLIENT_free, NULL), GB_METHOD("Stop", NULL, CDNSCLIENT_Stop, NULL), GB_METHOD("GetHostName", NULL, CDNSCLIENT_GetHostName, NULL), GB_METHOD("GetHostIP", NULL, CDNSCLIENT_GetHostIP, NULL), GB_PROPERTY("HostName", "s", HostName), GB_PROPERTY("HostIP", "s", HostIP), GB_PROPERTY("Async", "b", CDNSCLIENT_Async), GB_PROPERTY_READ("Status", "i", CDNSCLIENT_Status), GB_CONSTANT("_Properties", "s", "HostName,HostIP,Async=TRUE"), GB_CONSTANT("_DefaultEvent", "s", "Finished"), GB_END_DECLARE }; /****************************************************************************** I do not know if Solaris accepts getaddrinfo and getnameinfo, so here is the old implementation for that O.S. *******************************************************************************/ /* void* dns_get_ip(void* v_obj) { char Buf[1]; char *BufData; int myid; int herr; struct hostent hostbuf, *stHost; size_t hstbuflen; char tmphstbuf[1024]; CDNSCLIENT *mythis; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); hstbuflen=1024; Buf[0]='1'; mythis=(CDNSCLIENT*)v_obj; sem_wait(&mythis->sem_id); myid=mythis->i_id; sem_post(&mythis->sem_id); stHost=gethostbyname_r (mythis->sHostName, &hostbuf, tmphstbuf, hstbuflen, &herr); sem_wait(&dns_th_pipe); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); write (dns_w_pipe,&v_obj,sizeof(void*)); write (dns_w_pipe,&myid,sizeof(int)); write (dns_w_pipe,Buf,sizeof(char)); if (stHost!=NULL) { BufData=inet_ntoa(*( (struct in_addr*)stHost->h_addr )); write (dns_w_pipe,BufData,strlen(BufData)*sizeof(char)); } write (dns_w_pipe,"\x10",sizeof(char)); sem_post(&dns_th_pipe); return NULL; } void* dns_get_name(void* v_obj) { char Buf[1]; int myid; int herr; size_t hstbuflen; char tmphstbuf[2048]; struct hostent hostbuf,*stHost=NULL; CDNSCLIENT *mythis; struct in_addr addr; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); hstbuflen=2048; Buf[0]='0'; mythis=(CDNSCLIENT*)v_obj; sem_wait(&mythis->sem_id); myid=mythis->i_id; sem_post(&mythis->sem_id); inet_aton(mythis->sHostIP,&addr); stHost=gethostbyaddr_r((const char *)&addr, sizeof (addr), AF_INET, &hostbuf,tmphstbuf,hstbuflen,&herr); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); sem_wait(&dns_th_pipe); write (dns_w_pipe,&v_obj,sizeof(void*)); write (dns_w_pipe,&myid,sizeof(int)); write (dns_w_pipe,Buf,sizeof(char)); if (stHost!=NULL) write (dns_w_pipe,stHost->h_name,strlen(stHost->h_name)*sizeof(char)); write (dns_w_pipe,"\x10",sizeof(char)); sem_post(&dns_th_pipe); return NULL; } */