Mail Archives: cygwin/2004/04/15/08:52:45
Another self-followup :-)
----- Original Message -----
From: "Enzo Michelangeli" <em AT em DOT no-ip DOT com>
To: <cygwin AT cygwin DOT com>
Cc: ""Brian Ford"" <ford AT vss DOT fsi DOT com>
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):
-------------------------------------------
em AT emnb ~/unsafesocket
$ cc -o gh ghmain.c gethostbyname_r.c
em AT emnb ~/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
em AT emnb ~/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/
- Raw text -