From john@ulantris.csci.unt.edu Date: Wed, 6 Sep 1995 11:53:15 -0500 (CDT) From: "John A. Booth" <john@ulantris.csci.unt.edu> To: Derek Snider <derek@idirect.com> Cc: merc-l@webnexus.com Subject: Re: gethostbyname > > We are currently having a snall problem when gethostbyname receives > > certain ip adresses it cannot resolve immediately, and it blocks for > > about 2 minutes (which of course means the mud stops running) > > We are running Rom 2.3 base on FreeBSD 2.0 > > Is there some kind of timelimit i can specify for gethostbyname or should > > i think about spawning the call to a child process... > > I'm considering the child process route myself... and also making it handle > IDENT/AUTH protocol. > Basically, when the mud starts up (the sooner you fork the better), open a > pipe, fork off and exec the child process, and use the pipe to communicate. > You can send off requests to the child... ie: "resolve ###.###.###.###", > or "ident ##### ##### ###.###.###.###"... and the child can do the slow work, > and send the info back to the server when it's done. This is exactly what we have going. This way there's no lag caused by name resolving--the child process lags, this just results in # ip's sitting around for a bit, but we still have name resolving--it may take 20 sec - 1 min or so for some names...but heh.....you should be able to get an idea from these code snippets....you can see the whole source on anon ftp ulantris.csci.unt.edu (soon to be ulantris.compsci.com). This is only for example, you probably can't just throw it in easily--the child process is functional-you can use it relatively easily....don't ask me any questions this info is just FYI.... /* snippet from comm.c that execs the sub process */ sprintf( log_buf, "ROM is ready to rock on port %d.", port ); log_string( log_buf ); if(pipe(fd1)<0||pipe(fd2) < 0 ) log_string("pipe error"); if(pipe(fd1)<0 || pipe(fd2) < 0) log_string("pipe error"); if((pid=fork())<0) log_string("fork error"); else if (pid>0) { close(fd1[0]); close(fd2[1]); fcntl(fd1[0],F_SETFL,O_NONBLOCK); fcntl(fd2[0],F_SETFL,O_NONBLOCK); game_loop_unix( control,fd1[1],fd2[0] ); close (control); log_string( "Normal termination of game." ); exit(0); return (0); } else { close(fd1[1]); close(fd2[0]); if (fd1[0]!=STDIN_FILENO) { if (dup2(fd1[0],STDIN_FILENO) !=STDIN_FILENO) log_string("dup2 error to stdin"); close(fd1[0]); } if (fd2[1]!=STDOUT_FILENO) { if (dup2(fd2[1],STDOUT_FILENO) !=STDOUT_FILENO) log_string("dup2 error to stdout"); close(fd2[1]); } if (execl("./getnameauth","getnameauth",(char *)0)<0) log_string("execl error"); } /* End starting the sub process */ /* Check the pipe if there's any input --ignore the sanity_check stuff */ /* * Process input. */ sanity_check(descriptor_list,descriptor_list_sanity,"comm.c line 852"); for ( d = descriptor_list; d != NULL; d = d_next ) { sanity_check(d->next,d->sanity_next,"comm.c line 855"); d_next = d->next; d->fcommand = FALSE; if (fix_resolve) { fprintf(stderr,"Reading from pipe"); if ((n=read(read_pipe,line,MAXLINE))<0) { log_string("read error from pipe"); } else { fprintf(stderr,"From Pipe: %s\n\r",line); if (n==0) { log_string("child closed pipe"); } else { resolve_ok=TRUE; fix_resolve=FALSE; } } } if (d->host_name_state) { if (resolve_ok && d->character) { resolve_ok=FALSE; free(CurrentlyResolving); CurrentlyResolving=strdup(d->character->name); addr = ntohl( d->address); sprintf( addr_buff, "Resolving@%d.%d.%d.%d", ( addr >> 24 ) & 0xFF, ( addr >> 16 ) & 0xFF, ( addr >> 8 ) & 0xFF, ( addr ) & 0xFF ); free(d->host); d->host=strdup(addr_buff); d->host_name_state=IN_PROGRESS; sprintf(addr_buff," %s %d %d",d->host,d->address,d->port); log_string(addr_buff); sprintf(addr_buff,"%s %d %d %d",d->character->name,d->address,d->port,port); fprintf(stderr,"Writing to pipe!"); if (write(write_pipe,addr_buff,strlen(addr_buff)) !=strlen(addr_buff)) log_string("write error to pipe"); } else if (d->host_name_state==IN_PROGRESS) { if (d->timer<1) { fprintf(stderr,"Reading from pipe"); if ((n=read(read_pipe,line,MAXLINE))<0) { d->timer=100; log_string("read error from pipe"); } else { fprintf(stderr,"From Pipe: %s\n\r",line); d->host_name_state=0; if (n==0) { log_string("child closed pipe"); } else { if (d->character /* && strncasecmp(d->character->name,line,strlen(d->character->name))==0 */) { fprintf(stderr,"COMMDEBUG: Setting line[%d] to 0\n",n); line[n]=0; free(d->host); d->host=strdup(line+strlen(d->character->name)); sprintf(addr_buff," %s %d %d",d->host,d->address,d->port); log_string(addr_buff); resolve_ok=TRUE; } else { if (d->character) { d->host_name_state=IN_PROGRESS; log_string("name didn't match"); } else { resolve_ok=TRUE; log_string("Null char, turning resolve on "); } } } } } /* if d->timer */ else d->timer--; } /* if in-progress */ } /* End of checking pipe */ This is the child process. /* getnameauth.c */ #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <netinet/in.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #define MAXLINE 4096 #define SIZ 500 /* various buffers */ extern int errno; char * auth_tcpuser(register long unsigned int in, register short unsigned int local, register short unsigned int remote); unsigned short auth_tcpport = 113; void err_sys( const char *str ) { FILE *log_file; if ( ( log_file=fopen("./err.log","a+t"))==NULL ) { perror("can't open Error log!"); /* exit(1); */ } fprintf(log_file, "%s\n", str ); fclose(log_file); return; } int main(void) { int n, int1, int2,addr,server_port; struct hostent *from; char *user; char line[MAXLINE]; char addr_buff[64]; char name_buff[64]; while ( (n = read(STDIN_FILENO, line, MAXLINE)) > 0) { line[n]=0; if (sscanf(line,"%s %d %d %d",&name_buff,&int1,&int2,&server_port)==4) { fprintf(stderr,"%d \n",int1); from=gethostbyaddr( (char*)&int1,sizeof(int1),AF_INET); user=auth_tcpuser(int1,server_port,int2); err_sys(user); addr = ntohl(int1); sprintf( addr_buff, "%d.%d.%d.%d port %d: ", ( addr >> 24 ) & 0xFF, ( addr >> 16 ) & 0xFF, ( addr >> 8 ) & 0xFF, ( addr ) & 0xFF, int2); sprintf(line,"%s %s@%s",name_buff,user,from?from->h_name:addr_buff); n=strlen(line); if (write(STDOUT_FILENO,line,n) !=n) err_sys("write error"); } else { if (write(STDOUT_FILENO,"invalid args\n",13)!=13) err_sys("write error"); } } exit(0); } static char ruser[SIZ]; static char realbuf[SIZ]; static char *buf; /* This was taken from public domain source that I managed to dredge up, * I changed a little of it, but I can't claim that I wrote it all. I don't * know who the original author is, so I can't give him/her credit. */ char * auth_tcpuser(register long unsigned int in, register short unsigned int local, register short unsigned int remote) { struct sockaddr_in sa; register int s; register int buflen; register int w; register int saveerrno; char ch; unsigned short rlocal; unsigned short rremote; if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) return 0; sa.sin_family = AF_INET; sa.sin_port = htons(auth_tcpport); sa.sin_addr.s_addr = in; if (connect(s, (struct sockaddr *)&sa, sizeof(sa)) == -1) { saveerrno = errno; (void) close(s); errno = saveerrno; return("UNKNOWN"); } buf = realbuf; (void) sprintf(buf, "%u , %u\r\n", (unsigned int) remote, (unsigned int) local); /* note the reversed order, the example in the RFC is misleading */ buflen = strlen(buf); while ((w = write(s, buf, buflen)) < buflen) if (w == -1) { /* should we worry about 0 as well? */ saveerrno = errno; (void) close(s); errno = saveerrno; return("SEND_ERROR"); } else { buf += w; buflen -= w; } buf = realbuf; while ((w = read(s, &ch, 1)) == 1) { *buf = ch; if ((ch != ' ') && (ch != '\t') && (ch != '\r')) ++buf; if ((buf - realbuf == sizeof(realbuf) - 1) || (ch == '\n')) break; } if (w == -1) { saveerrno = errno; (void) close(s); errno = saveerrno; return("READ_ERROR"); } *buf = '\0'; bzero(ruser,SIZ); /* can do a memset if you need to */ if (sscanf(realbuf, "%hd,%hd: USERID :%*[^:]:%s", &rremote, &rlocal, ruser) < 3) { (void) close(s); err_sys(ruser); errno = EOF; /* makes sense, right? well, not when USERID failed to match ERROR * but there's no good error to return in that case */ return("BAD_STRING"); } if ((remote != rremote) || (local != rlocal)) { (void) close(s); errno = EOF; return("REMOTE/LOCAL"); } (void) close(s); return ruser; } ============================================================================= / ______ _______ ____ _____ ___ __ _ ______ ____ ____ _____ / \ | ____|__ __| _ \ / ____\ / _ \| \ / | ____| / __ \| _ \ / ____\ \ / | |__ | | | |_| | | | |_| | |\/| | |___ | | | | |_| | | / / | ___| | | | ___/| | __| _ | | | | ____| | | | | __/| | ___ \ \ | | | | | | | |___| | | | | | | | |____ | |__| | |\ \| |___| | / / |_| |_| |_| o \_____/|_| |_|_| |_|______|o \____/|_| \_|\_____/ \ \ / ============================================================================ ------------------------------------------------------------------------------ ftp://ftp.game.org/pub/mud FTP.GAME.ORG http://www.game.org/ftpsite/ ------------------------------------------------------------------------------ This archive came from FTP.GAME.ORG, the ultimate source for MUD resources. ------------------------------------------------------------------------------