Another self-followup :-) ----- Original Message ----- From: "Enzo Michelangeli" <[EMAIL PROTECTED]> To: <[EMAIL PROTECTED]> Cc: ""Brian Ford"" <[EMAIL PROTECTED]> Sent: Thursday, April 15, 2004 12:03 PM Subject: Re: 1.5.9-1: socket() appears NOT to be thread-safe
> P.S. By the way, Corinna: couldn't I just put my gethostbyname_r() in > the public domain, rather than going through the bureaucratic chore of > the copyright assignment? Also because I feel that implementing it > through mutex-protection of gethostbyname(), as I did, is just a quick > hack, as it unnecessarily blocks other threads that could access the > name server in parallel (with separate network I/O and properly > re-entrant code). It may help other implementors to solve an urgent > problem, but I don't think it should be released as part of the Cygwin > code. Well, OK, here is the code, hereby placed in the public domain. Everybody can do with it whatever s/he likes; attribution will be appreciated. Of course, no guarantees etc. On an unrelated matter: I noticed that when a Cygwin application listens on a TCP port, it also listens on a random TCP port on 127.0.0.1 . What is the reason for that? I'm new to cygwin programming, and initially I was suspecting a bug in my application, but then I saw that netcat was doing the same thing... According to my Kerio firewall, the program opens a connection to that port when it receives a signal (either from a Ctrl-C or from a "kill" issued on another console.). Enzo ==================================================================== All the code in this message is hereby put in the public domain (http://www.gnu.org/philosophy/categories.html#PublicDomainSoftware ). Results of test program (for the code see further below): ------------------------------------------- [EMAIL PROTECTED] ~/unsafesocket $ cc -o gh ghmain.c gethostbyname_r.c [EMAIL PROTECTED] ~/unsafesocket $ ./gh n NOT protecting socket() call with mutex: Thread 0: host information for mailin-00.mx.aol.com. not found - h_errno: 1 Thread 1: mailin-01.mx.aol.com 205.188.156.185 205.188.159.57 64.12.137.89 64.12.138.57 Aliases: Thread 2: mailin-02.mx.aol.com 205.188.156.249 205.188.159.217 64.12.137.121 64.12.138.89 Aliases: Thread 3: www.yahoo.akadns.net 66.94.230.43 66.94.230.34 66.94.230.32 66.94.230.36 66.94.230.41 66.94.230.42 66.94.230.33 66.94.230.38 Aliases: www.yahoo.com [EMAIL PROTECTED] ~/unsafesocket $ (the results are the same with "./gh y", which protects the call to gethostbyname_r through an additional mutex lock in the main(). That shows that gethostbyname_r() is already thread-safe by itself). ------------------------------------------- --------- begin gethostbyname_r.h --------- /* including netdb.h suppresses warnings from the use of hostent */ #include <netdb.h> #ifndef __LINUX__ #define LOCAL_GETHOSTBYNAME_R int gethostbyname_r (const char *name, struct hostent *ret, char *buf, size_t buflen, struct hostent **result, int *h_errnop); #endif /* gethostbyname_r */ --------- end gethostbyname_r.h --------- --------- begin gethostbyname_r.c --------- #include <netdb.h> #include <sys/socket.h> #include "gethostbyname_r.h" #include <string.h> #include <pthread.h> #ifdef LOCAL_GETHOSTBYNAME_R /* duh? ERANGE value copied from web... */ #define ERANGE 34 int gethostbyname_r (const char *name, struct hostent *ret, char *buf, size_t buflen, struct hostent **result, int *h_errnop) { int hsave; struct hostent *ph; static pthread_mutex_t __mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&__mutex); /* begin critical area */ hsave = h_errno; ph = gethostbyname(name); *h_errnop = h_errno; /* copy h_errno to *h_herrnop */ if (ph == NULL) { *result = NULL; } else { char **p, **q; char *pbuf; int nbytes=0; int naddr=0, naliases=0; /* determine if we have enough space in buf */ /* count how many addresses */ for (p = ph->h_addr_list; *p != 0; p++) { nbytes += ph->h_length; /* addresses */ nbytes += sizeof(*p); /* pointers */ naddr++; } nbytes += sizeof(*p); /* one more for the terminating NULL */ /* count how many aliases, and total length of strings */ for (p = ph->h_aliases; *p != 0; p++) { nbytes += (strlen(*p)+1); /* aliases */ nbytes += sizeof(*p); /* pointers */ naliases++; } nbytes += sizeof(*p); /* one more for the terminating NULL */ /* here nbytes is the number of bytes required in buffer */ /* as a terminator must be there, the minimum value is ph->h_length */ if(nbytes > buflen) { *result = NULL; pthread_mutex_unlock(&__mutex); /* end critical area */ return ERANGE; /* not enough space in buf!! */ } /* There is enough space. Now we need to do a deep copy! */ /* Allocation in buffer: from [0] to [(naddr-1) * sizeof(*p)]: pointers to addresses at [naddr * sizeof(*p)]: NULL from [(naddr+1) * sizeof(*p)] to [(naddr+naliases) * sizeof(*p)] : pointers to aliases at [(naddr+naliases+1) * sizeof(*p)]: NULL then naddr addresses (fixed length), and naliases aliases (asciiz). */ *ret = *ph; /* copy whole structure (not its address!) */ /* copy addresses */ q = (char **)buf; /* pointer to pointers area (type: char **) */ ret->h_addr_list = q; /* update pointer to address list */ pbuf = buf + ((naddr+naliases+2)*sizeof(*p)); /* skip that area */ for (p = ph->h_addr_list; *p != 0; p++) { memcpy(pbuf, *p, ph->h_length); /* copy address bytes */ *q++ = pbuf; /* the pointer is the one inside buf... */ pbuf += ph->h_length; /* advance pbuf */ } *q++ = NULL; /* address list terminator */ /* copy aliases */ ret->h_aliases = q; /* update pointer to aliases list */ for (p = ph->h_aliases; *p != 0; p++) { strcpy(pbuf, *p); /* copy alias strings */ *q++ = pbuf; /* the pointer is the one inside buf... */ pbuf += strlen(*p); /* advance pbuf */ *pbuf++ = 0; /* string terminator */ } *q++ = NULL; /* terminator */ strcpy(pbuf, ph->h_name); /* copy alias strings */ ret->h_name = pbuf; pbuf += strlen(ph->h_name); /* advance pbuf */ *pbuf++ = 0; /* string terminator */ *result = ret; /* and let *result point to structure */ } h_errno = hsave; /* restore h_errno */ pthread_mutex_unlock(&__mutex); /* end critical area */ return (*result == NULL); } #endif /* LOCAL_GETHOSTBYNAME_R */ --------- end gethostbyname_r.c --------- --------- begin ghmain.c ------------------ #include <sys/socket.h> #include <netinet/in.h> #include <errno.h> #include <stdio.h> #include <string.h> #include <pthread.h> #include <stdlib.h> #include "gethostbyname_r.h" #define MAXTHREADS 4 static int sync_with_mutex = 0; struct thrdata { int thrs; pthread_t t; char *name; int gh_status; int gh_h_errno; struct hostent *gh_ph; } th[MAXTHREADS]; static void *thread_code(void *p); main(int argc, char *argv[]) { int i; char s[80]; if(argc == 1) { printf("usage: %s [n|y]\n", argv[0]); exit(1); } if(argv[1][0] == 'y' || argv[1][0] == 'Y') { printf("Protecting socket() call with mutex:\n"); sync_with_mutex = 1; } else { printf("NOT protecting socket() call with mutex:\n"); sync_with_mutex = 0; } for(i=0; i<MAXTHREADS; i++) { if(i < MAXTHREADS-1) { sprintf(s, "mailin-%02d.mx.aol.com.", i); th[i].name = strdup(s); // freed at exit... } else // illustrates CNAME th[i].name = strdup("www.yahoo.com"); // freed at exit... pthread_create(&th[i].t, NULL, thread_code, (void *)&(th[i])); } for(i=0; i<MAXTHREADS; i++) { pthread_join(th[i].t, NULL); } for(i=0; i<MAXTHREADS; i++) { struct hostent *hp; char **p; hp = th[i].gh_ph; printf("Thread %d: \n", i); if (hp == NULL) { printf("host information for %s not found - h_errno: %d\n", th[i].name, th[i].gh_h_errno); continue; } printf("%s\n", hp->h_name); for (p = hp->h_addr_list; *p != 0; p++) { struct in_addr in; memcpy(&in.s_addr, *p, sizeof (in.s_addr)); printf("\t%s\n", inet_ntoa(in)); } printf(" Aliases:\n"); for (p = hp->h_aliases; *p != 0; p++) { (void) printf("\t%s", *p); putchar('\n'); } } exit(0); } static void *thread_code(void *p) { struct thrdata *pt = (struct thrdata *)p; struct hostent *phe = malloc(sizeof(struct hostent)); // freed at exit... char *buf = malloc(4096); // freed at exit... struct hostent *result; int local_h_errno; int status; static pthread_mutex_t __mutex = PTHREAD_MUTEX_INITIALIZER; if(sync_with_mutex) { pthread_mutex_lock(&__mutex); /* begin critical area */ } status = gethostbyname_r(pt->name, phe, buf, 4096, &result, &local_h_errno); if(sync_with_mutex) { pthread_mutex_unlock(&__mutex); /* end critical area */ } /* report results to main thread */ pt->gh_status = status; pt->gh_h_errno = local_h_errno; pt->gh_ph = result; return(NULL); } --------- end ghmain.c ------------------ -- Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple Problem reports: http://cygwin.com/problems.html Documentation: http://cygwin.com/docs.html FAQ: http://cygwin.com/faq/