/*
* This file contains the socket code, used for accepting
* new connections as well as reading and writing to
* sockets, and closing down unused sockets.
*/
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <event2/event.h>
#include <event2/buffer.h>
#include <event2/bufferevent.h>
/* including main header file */
#include "mud.h"
/* global variables */
struct event_base *io_base;
evutil_socket_t io_sock;
struct event *io_sock_event;
D_SOCKET *dsock_free = NULL; /* the socket free list */
D_SOCKET *dsock_list = NULL; /* the linked list of active sockets */
D_MOBILE *dmobile_free = NULL; /* the mobile free list */
D_MOBILE *dmobile_list = NULL; /* the mobile list of active mobiles */
/* mccp support */
#ifndef NOMCCP
const unsigned char compress_will [] = { IAC, WILL, TELOPT_COMPRESS, '\0' };
const unsigned char compress_will2 [] = { IAC, WILL, TELOPT_COMPRESS2, '\0' };
#endif
const unsigned char do_echo [] = { IAC, WONT, TELOPT_ECHO, '\0' };
const unsigned char dont_echo [] = { IAC, WILL, TELOPT_ECHO, '\0' };
#ifdef CYGWIN32
pthread_mutex_t lookup_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
/*
* This is where it all starts, nothing special.
*/
int main(int argc, char **argv)
{
bool copyover = FALSE;
/* note startup */
log_string("Program starting.");
if (argc > 2 && atoi(argv[2]) > 0)
{
copyover = TRUE;
io_sock = atoi(argv[2]);
}
/* init */
init_io(copyover);
/* load */
load_muddata(copyover);
/* run */
event_base_dispatch(io_base);
/* kill */
kill_io();
/* note shutdown */
log_string("Program terminated without errors.");
return 0;
}
void prompt(D_SOCKET *dsock)
{
/* bust a prompt */
if (dsock->state == STATE_PLAYING && dsock->bust_prompt)
{
text_to_buffer(dsock, "EventMud:> ");
dsock->bust_prompt = FALSE;
}
}
/*
* Set of callback routines to handle socket and timer events
*/
void cb_recycle(evutil_socket_t sockfd, short flags, void *arg)
{
struct timeval tv;
recycle_sockets();
/* reschedule the recycle timer */
tv.tv_sec = 10;
tv.tv_usec = 0;
event_base_once(io_base, -1, EV_TIMEOUT, cb_recycle, NULL, &tv);
}
void cb_update(evutil_socket_t sockfd, short flags, void *arg)
{
struct timeval tv;
update_handler();
/* reschedule the update timer */
tv.tv_sec = 0;
tv.tv_usec = 1000000 / PULSES_PER_SECOND;
event_base_once(io_base, -1, EV_TIMEOUT, cb_update, NULL, &tv);
}
void cb_command(evutil_socket_t sockfd, short flags, void *arg)
{
D_SOCKET *dsock = arg;
if (dsock->state == STATE_CLOSED)
{
return;
}
/* check for pending command */
if (dsock->next_command[0] != '\0')
{
struct timeval tv;
process_cmd(dsock);
retrieve_cmd(dsock);
/* reschedule command timer */
tv.tv_sec = 0;
tv.tv_usec = 1000000 / PULSES_PER_SECOND;
event_base_once(io_base, -1, EV_TIMEOUT, cb_command, (void*) dsock, &tv);
}
/* bust a prompt */
prompt(dsock);
}
void cb_read(struct bufferevent *event, void *arg)
{
D_SOCKET *dsock = arg;
/* transfer to input buffer - could be optimized not to */
struct evbuffer *buffer = bufferevent_get_input(event);
int size = strlen(dsock->inbuf);
int left = sizeof(dsock->inbuf) - size - 1;
int read = evbuffer_remove(buffer, (void*) (dsock->inbuf + size), left);
dsock->inbuf[size + read] = '\0';
/* discard the remainder */
evbuffer_drain(buffer, evbuffer_get_length(buffer));
/* check for pending command */
if (dsock->next_command[0] == '\0')
{
retrieve_cmd(dsock);
cb_command(-1, 0, arg);
}
}
void cb_write(struct bufferevent *event, void *arg)
{
D_SOCKET *dsock = arg;
/* bust a prompt */
prompt(dsock);
}
void cb_error(struct bufferevent *event, short flags, void *arg)
{
D_SOCKET *dsock = arg;
/* disconnect */
close_socket(dsock, FALSE);
}
void cb_accept(evutil_socket_t sockfd, short flags, void *arg)
{
struct sockaddr_storage ss;
socklen_t slen = sizeof(ss);
int fd;
/* connect */
fd = accept(sockfd, (struct sockaddr*) &ss, &slen);
evutil_make_socket_nonblocking(fd);
new_socket(fd);
}
/*
* io startup and shutdown functions
*/
void init_io(bool copyover)
{
struct timeval tv;
/* initialize io */
io_base = event_base_new();
/* open game socket */
if (copyover == FALSE)
{
struct sockaddr_in sin;
io_sock = socket(AF_INET, SOCK_STREAM, 0);
evutil_make_socket_nonblocking(io_sock);
evutil_make_listen_socket_reuseable(io_sock);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(MUDPORT);
bind(io_sock, (struct sockaddr*) &sin, sizeof(struct sockaddr));
listen(io_sock, 3);
}
/* register accept handler */
io_sock_event = event_new(io_base, io_sock, (EV_READ | EV_PERSIST), cb_accept, NULL);
event_add(io_sock_event, NULL);
/* schedule recycle timer */
tv.tv_sec = 10;
tv.tv_usec = 0;
event_base_once(io_base, -1, EV_TIMEOUT, cb_recycle, NULL, &tv);
/* schedule update timer */
tv.tv_sec = 0;
tv.tv_usec = 1000000 / PULSES_PER_SECOND;
event_base_once(io_base, -1, EV_TIMEOUT, cb_update, NULL, &tv);
}
void kill_io()
{
D_SOCKET *dsock, *dsock_next;
/* close user sockets */
for (dsock = dsock_list; dsock; dsock = dsock_next)
{
dsock_next = dsock->next;
if (dsock->lookup_status == STATE_CLOSED) continue;
close_socket(dsock, FALSE);
}
/* deregister accept handler */
event_del(io_sock_event);
event_free(io_sock_event);
/* close game socket */
close(io_sock);
/* release io */
event_base_free(io_base);
}
/*
* New_socket()
*
* Initializes a new socket, get's the hostname
* and puts it in the active socket_list.
*/
void new_socket(int fd)
{
D_SOCKET * dsock;
struct sockaddr_in sock_addr;
pthread_t thread_lookup;
LOOKUP_DATA * lData;
socklen_t size;
pthread_attr_t attr;
/* initialize threads */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
/*
* allocate some memory for a new socket if
* there is no free socket in the free_list
*/
if (dsock_free == NULL)
{
if ((dsock = (D_SOCKET *) malloc(sizeof(D_SOCKET))) == NULL)
{
bug("New_socket: Cannot allocate memory for socket.");
abort();
}
}
else
{
dsock = dsock_free;
dsock_free = dsock_free->next;
}
/* reset the socket */
reset_socket(dsock, fd);
/* update the linked list of sockets */
dsock->next = dsock_list;
dsock_list = dsock;
/* do a host lookup */
size = sizeof(sock_addr);
if (getpeername(fd, (struct sockaddr *) &sock_addr, &size) < 0)
{
perror("New_socket: getpeername");
dsock->hostname = strdup("unknown");
}
else
{
/* set the IP number as the temporary hostname */
dsock->hostname = strdup(inet_ntoa(sock_addr.sin_addr));
if (!compares(dsock->hostname, "127.0.0.1"))
{
/* allocate some memory for the lookup data */
if ((lData = malloc(sizeof(*lData))) == NULL)
{
bug("New_socket: Cannot allocate memory for lookup data.");
abort();
}
/* Set the lookup_data for use in lookup_address() */
lData->buf = strdup((char *) &sock_addr.sin_addr);
lData->dsock = dsock;
/* dispatch the lookup thread */
pthread_create(&thread_lookup, &attr, &lookup_address, (void*) lData);
}
else dsock->lookup_status++;
}
/* negotiate compression */
#ifndef NOMCCP
text_to_buffer(dsock, (char *) compress_will2);
text_to_buffer(dsock, (char *) compress_will);
#endif
/* send the greeting */
text_to_buffer(dsock, greeting);
text_to_buffer(dsock, "What is your name? ");
}
/*
* Close_socket()
*
* Will close one socket directly, freeing all
* resources and making the socket availably on
* the socket free_list.
*/
void close_socket(D_SOCKET *dsock, bool reconnect)
{
if (dsock->lookup_status > TSTATE_DONE) return;
dsock->lookup_status += 2;
/* remove the socket */
if (dsock->state == STATE_PLAYING)
{
if (reconnect)
text_to_socket(dsock, "This connection has been taken over.\n\r");
else if (dsock->player)
{
dsock->player->socket = NULL;
log_string("Closing link to %s", dsock->player->name);
}
}
else if (dsock->player)
free_mobile(dsock->player);
/* set the closed state */
close(dsock->control);
dsock->state = STATE_CLOSED;
/* release user context */
bufferevent_free(dsock->context);
dsock->context = NULL;
}
void reset_socket(D_SOCKET *sock_new, int fd)
{
struct bufferevent *event;
/* reset the structure */
bzero(sock_new, sizeof(*sock_new));
sock_new->control = fd;
sock_new->state = STATE_NEW_NAME;
sock_new->lookup_status = TSTATE_LOOKUP;
/* allocate user context */
event = (void*) bufferevent_socket_new(io_base, fd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(event, cb_read, cb_write, cb_error, (void*) sock_new);
bufferevent_setwatermark(event, EV_READ, 0, 4096);
bufferevent_enable(event, (EV_READ | EV_WRITE));
sock_new->context = event;
}
/*
* Text_to_buffer()
*
* Stores outbound text in a buffer, where it will
* stay untill it is flushed in the gameloop.
*
* Will also parse ANSI colors and other tags.
*/
void text_to_buffer(D_SOCKET *dsock, const char *txt)
{
static char output[8 * MAX_BUFFER];
bool underline = FALSE, bold = FALSE;
int iPtr = 0, last = -1, j, k;
int length = strlen(txt);
/* the color struct */
struct sAnsiColor
{
const char cTag;
const char * cString;
int aFlag;
};
/* the color table... */
const struct sAnsiColor ansiTable[] =
{
{ 'd', "30", eTHIN },
{ 'D', "30", eBOLD },
{ 'r', "31", eTHIN },
{ 'R', "31", eBOLD },
{ 'g', "32", eTHIN },
{ 'G', "32", eBOLD },
{ 'y', "33", eTHIN },
{ 'Y', "33", eBOLD },
{ 'b', "34", eTHIN },
{ 'B', "34", eBOLD },
{ 'p', "35", eTHIN },
{ 'P', "35", eBOLD },
{ 'c', "36", eTHIN },
{ 'C', "36", eBOLD },
{ 'w', "37", eTHIN },
{ 'W', "37", eBOLD },
/* the end tag */
{ '\0', "", eTHIN }
};
if (length >= MAX_BUFFER)
{
log_string("text_to_buffer: buffer overflow.");
return;
}
/* always start with a leading space */
if (dsock->top_output == 0)
{
dsock->outbuf[0] = '\n';
dsock->outbuf[1] = '\r';
dsock->top_output = 2;
}
while (*txt != '\0')
{
/* simple bound checking */
if (iPtr > (8 * MAX_BUFFER - 15))
break;
switch(*txt)
{
default:
output[iPtr++] = *txt++;
break;
case '#':
txt++;
/* toggle underline on/off with #u */
if (*txt == 'u')
{
txt++;
if (underline)
{
underline = FALSE;
output[iPtr++] = 27; output[iPtr++] = '['; output[iPtr++] = '0';
if (bold)
{
output[iPtr++] = ';'; output[iPtr++] = '1';
}
if (last != -1)
{
output[iPtr++] = ';';
for (j = 0; ansiTable[last].cString[j] != '\0'; j++)
{
output[iPtr++] = ansiTable[last].cString[j];
}
}
output[iPtr++] = 'm';
}
else
{
underline = TRUE;
output[iPtr++] = 27; output[iPtr++] = '[';
output[iPtr++] = '4'; output[iPtr++] = 'm';
}
}
/* parse ## to # */
else if (*txt == '#')
{
txt++;
output[iPtr++] = '#';
}
/* #n should clear all tags */
else if (*txt == 'n')
{
txt++;
if (last != -1 || underline || bold)
{
underline = FALSE;
bold = FALSE;
output[iPtr++] = 27; output[iPtr++] = '[';
output[iPtr++] = '0'; output[iPtr++] = 'm';
}
last = -1;
}
/* check for valid color tag and parse */
else
{
bool validTag = FALSE;
for (j = 0; ansiTable[j].cString[0] != '\0'; j++)
{
if (*txt == ansiTable[j].cTag)
{
validTag = TRUE;
/* we only add the color sequence if it's needed */
if (last != j)
{
bool cSequence = FALSE;
/* escape sequence */
output[iPtr++] = 27; output[iPtr++] = '[';
/* remember if a color change is needed */
if (last == -1 || last / 2 != j / 2)
cSequence = TRUE;
/* handle font boldness */
if (bold && ansiTable[j].aFlag == eTHIN)
{
output[iPtr++] = '0';
bold = FALSE;
if (underline)
{
output[iPtr++] = ';'; output[iPtr++] = '4';
}
/* changing to eTHIN wipes the old color */
output[iPtr++] = ';';
cSequence = TRUE;
}
else if (!bold && ansiTable[j].aFlag == eBOLD)
{
output[iPtr++] = '1';
bold = TRUE;
if (cSequence)
output[iPtr++] = ';';
}
/* add color sequence if needed */
if (cSequence)
{
for (k = 0; ansiTable[j].cString[k] != '\0'; k++)
{
output[iPtr++] = ansiTable[j].cString[k];
}
}
output[iPtr++] = 'm';
}
/* remember the last color */
last = j;
}
}
/* it wasn't a valid color tag */
if (!validTag)
output[iPtr++] = '#';
else
txt++;
}
break;
}
}
/* and terminate it with the standard color */
if (last != -1 || underline || bold)
{
output[iPtr++] = 27; output[iPtr++] = '[';
output[iPtr++] = '0'; output[iPtr++] = 'm';
}
output[iPtr] = '\0';
/* check to see if the socket can accept that much data */
if (dsock->top_output + iPtr >= MAX_OUTPUT)
{
bug("Text_to_buffer: ouput overflow on %s.", dsock->hostname);
return;
}
/* add data to buffer */
strcpy(dsock->outbuf + dsock->top_output, output);
dsock->top_output += iPtr;
/* reset the top pointer */
dsock->top_output = 0;
/* queue onto socket */
text_to_socket(dsock, dsock->outbuf);
}
/*
* Text_to_socket()
*
* Sends text directly to the socket,
* will compress the data if needed.
*/
bool text_to_socket(D_SOCKET *dsock, const char *txt)
{
int iBlck, iPtr, iWrt = 0, length;
length = strlen(txt);
/* write compressed */
#ifndef NOMCCP
if (dsock && dsock->out_compress)
{
dsock->out_compress->next_in = (unsigned char *) txt;
dsock->out_compress->avail_in = length;
while (dsock->out_compress->avail_in)
{
dsock->out_compress->avail_out = COMPRESS_BUF_SIZE - (dsock->out_compress->next_out - dsock->out_compress_buf);
if (dsock->out_compress->avail_out)
{
int status = deflate(dsock->out_compress, Z_SYNC_FLUSH);
if (status != Z_OK)
return FALSE;
}
length = dsock->out_compress->next_out - dsock->out_compress_buf;
if (length > 0)
{
for (iPtr = 0; iPtr < length; iPtr += iWrt)
{
iBlck = UMIN(length - iPtr, 4096);
if (bufferevent_write((struct bufferevent*) dsock->context, (void*) (dsock->out_compress_buf + iPtr), iBlck) < 0)
{
bug("Text_to_socket (compressed): bufferevent_write() failed");
return FALSE;
}
iWrt = iBlck;
}
if (iWrt <= 0)
break;
if (iPtr > 0)
{
if (iPtr < length)
memmove(dsock->out_compress_buf, dsock->out_compress_buf + iPtr, length - iPtr);
dsock->out_compress->next_out = dsock->out_compress_buf + length - iPtr;
}
}
}
return TRUE;
}
#endif
/* write uncompressed */
for (iPtr = 0; iPtr < length; iPtr += iWrt)
{
iBlck = UMIN(length - iPtr, 4096);
if (bufferevent_write((struct bufferevent*) dsock->context, (void*) (txt + iPtr), iBlck) < 0)
{
bug("Text_to_socket: bufferevent_write() failed");
return FALSE;
}
iWrt = iBlck;
}
return TRUE;
}
/*
* Text_to_mobile()
*
* If the mobile has a socket, then the data will
* be send to text_to_buffer().
*/
void text_to_mobile(D_MOBILE *dMob, const char *txt)
{
if (dMob->socket)
{
text_to_buffer(dMob->socket, txt);
dMob->socket->bust_prompt = TRUE;
}
}
void retrieve_cmd(D_SOCKET *dsock)
{
int size = 0, i = 0, j = 0;
#ifndef NOMCCP
int telopt = 0;
#endif
/* if theres already a command ready, we return */
if (dsock->next_command[0] != '\0')
return;
/* if there is nothing pending, then return */
if (dsock->inbuf[0] == '\0')
return;
/* check how long the next command is */
while (dsock->inbuf[size] != '\0' && dsock->inbuf[size] != '\n' && dsock->inbuf[size] != '\r')
size++;
/* we only deal with real commands - but treat a full buffer as a command */
if (size < (sizeof(dsock->inbuf) - 1) && dsock->inbuf[size] == '\0')
return;
/* copy the next command into next_command */
for ( ; i < size; i++)
{
#ifndef NOMCCP
if (dsock->inbuf[i] == (signed char) IAC)
{
telopt = 1;
}
else if (telopt == 1 && (dsock->inbuf[i] == (signed char) DO || dsock->inbuf[i] == (signed char) DONT))
{
telopt = 2;
}
else if (telopt == 2)
{
telopt = 0;
if (dsock->inbuf[i] == (signed char) TELOPT_COMPRESS) /* check for version 1 */
{
if (dsock->inbuf[i-1] == (signed char) DO) /* start compressing */
compressStart(dsock, TELOPT_COMPRESS);
else if (dsock->inbuf[i-1] == (signed char) DONT) /* stop compressing */
compressEnd(dsock, TELOPT_COMPRESS, FALSE);
}
else if (dsock->inbuf[i] == (signed char) TELOPT_COMPRESS2) /* check for version 2 */
{
if (dsock->inbuf[i-1] == (signed char) DO) /* start compressing */
compressStart(dsock, TELOPT_COMPRESS2);
else if (dsock->inbuf[i-1] == (signed char) DONT) /* stop compressing */
compressEnd(dsock, TELOPT_COMPRESS2, FALSE);
}
}
else
#endif
if (isprint((int) dsock->inbuf[i]) && isascii((int) dsock->inbuf[i]))
{
dsock->next_command[j++] = dsock->inbuf[i];
}
}
dsock->next_command[j] = '\0';
/* skip forward to the next line */
while (dsock->inbuf[size] == '\n' || dsock->inbuf[size] == '\r')
{
dsock->bust_prompt = TRUE; /* seems like a good place to check */
size++;
}
/* use i as a static pointer */
i = size;
/* move the context of inbuf down */
while (dsock->inbuf[size] != '\0')
{
dsock->inbuf[size - i] = dsock->inbuf[size];
size++;
}
dsock->inbuf[size - i] = '\0';
}
void process_cmd(D_SOCKET *dsock)
{
/* figure out how to deal with the incoming command */
switch(dsock->state)
{
default:
break;
case STATE_NEW_NAME:
case STATE_NEW_PASSWORD:
case STATE_VERIFY_PASSWORD:
case STATE_ASK_PASSWORD:
handle_new_connections(dsock, dsock->next_command);
break;
case STATE_PLAYING:
handle_cmd_input(dsock, dsock->next_command);
break;
}
dsock->next_command[0] = '\0';
}
void handle_new_connections(D_SOCKET *dsock, char *arg)
{
D_MOBILE *p_new;
int i;
switch(dsock->state)
{
default:
bug("Handle_new_connections: Bad state.");
break;
case STATE_NEW_NAME:
if (dsock->lookup_status != TSTATE_DONE)
{
text_to_buffer(dsock, "Making a dns lookup, please have patience.\n\rWhat is your name? ");
return;
}
if (!check_name(arg)) /* check for a legal name */
{
text_to_buffer(dsock, "Sorry, that's not a legal name, please pick another.\n\rWhat is your name? ");
break;
}
arg[0] = toupper((int) arg[0]);
log_string("%s is trying to connect.", arg);
/* Check for a new Player */
if ((p_new = load_profile(arg)) == NULL)
{
if (dmobile_free == NULL)
{
if ((p_new = malloc(sizeof(*p_new))) == NULL)
{
bug("Handle_new_connection: Cannot allocate memory.");
abort();
}
}
else
{
p_new = dmobile_free;
dmobile_free = dmobile_free->next;
}
clear_mobile(p_new);
/* give the player it's name */
p_new->name = strdup(arg);
/* prepare for next step */
text_to_buffer(dsock, "Please enter a new password: ");
dsock->state = STATE_NEW_PASSWORD;
}
else /* old player */
{
/* prepare for next step */
text_to_buffer(dsock, "What is your password? ");
dsock->state = STATE_ASK_PASSWORD;
}
/* socket <-> player */
p_new->socket = dsock;
dsock->player = p_new;
break;
case STATE_NEW_PASSWORD:
if (strlen(arg) < 5 || strlen(arg) > 12)
{
text_to_buffer(dsock, "Between 5 and 12 chars please!\n\rPlease enter a new password: ");
return;
}
dsock->player->password = strdup(crypt(arg, dsock->player->name));
for (i = 0; dsock->player->password[i] != '\0'; i++)
{
if (dsock->player->password[i] == '~')
{
text_to_buffer(dsock, "Illegal password!\n\rPlease enter a new password: ");
return;
}
}
text_to_buffer(dsock, "Please verify the password: ");
dsock->state = STATE_VERIFY_PASSWORD;
break;
case STATE_VERIFY_PASSWORD:
if (compares(crypt(arg, dsock->player->name), dsock->player->password))
{
/* put him in the list */
dsock->player->next = dmobile_list;
dmobile_list = dsock->player;
log_string("New player: %s has entered the game.", dsock->player->name);
/* and into the game */
dsock->state = STATE_PLAYING;
text_to_buffer(dsock, motd);
}
else
{
free(dsock->player->password);
text_to_buffer(dsock, "Password mismatch!\n\rPlease enter a new password: ");
dsock->state = STATE_NEW_PASSWORD;
}
break;
case STATE_ASK_PASSWORD:
if (compares(crypt(arg, dsock->player->name), dsock->player->password))
{
if ((p_new = check_reconnect(dsock->player->name)) != NULL)
{
/* attach the new player */
ex_free_mob(dsock->player);
dsock->player = p_new;
p_new->socket = dsock;
log_string("%s has reconnected.", dsock->player->name);
/* and let him enter the game */
dsock->state = STATE_PLAYING;
text_to_buffer(dsock, "You take over a body already in use.\n\r");
}
else if ((p_new = load_player(dsock->player->name)) == NULL)
{
text_to_socket(dsock, "ERROR: Your pfile is missing!\n\r");
ex_free_mob(dsock->player);
dsock->player = NULL;
close_socket(dsock, FALSE);
return;
}
else
{
/* attach the new player */
ex_free_mob(dsock->player);
dsock->player = p_new;
p_new->socket = dsock;
/* put him in the active list */
p_new->next = dmobile_list;
dmobile_list = p_new;
log_string("%s has entered the game.", dsock->player->name);
/* and let him enter the game */
dsock->state = STATE_PLAYING;
text_to_buffer(dsock, motd);
}
}
else
{
text_to_socket(dsock, "Bad password!\n\r");
ex_free_mob(dsock->player);
dsock->player = NULL;
close_socket(dsock, FALSE);
}
break;
}
}
/* does the lookup, changes the hostname, and dies */
void *lookup_address(void *arg)
{
LOOKUP_DATA *lData = (LOOKUP_DATA *) arg;
struct hostent *from = 0;
#ifndef CYGWIN32
struct hostent ent;
char buf[16384];
int err;
#endif
#ifdef CYGWIN32
pthread_mutex_lock(&lookup_mutex);
from = gethostbyaddr(lData->buf, sizeof(lData->buf), AF_INET);
if (from && from->h_name)
{
free(lData->dsock->hostname);
lData->dsock->hostname = strdup(from->h_name);
}
pthread_mutex_unlock(&lookup_mutex);
#else
/* do the lookup and store the result at &from */
gethostbyaddr_r(lData->buf, sizeof(lData->buf), AF_INET, &ent, buf, 16384, &from, &err);
/* did we get anything ? */
if (from && from->h_name)
{
free(lData->dsock->hostname);
lData->dsock->hostname = strdup(from->h_name);
}
#endif
/* set it ready to be closed or used */
lData->dsock->lookup_status++;
/* free the lookup data */
free(lData->buf);
free(lData);
/* and kill the thread */
pthread_exit(0);
#ifdef CYGWIN32
return NULL;
#endif
}
void recycle_sockets()
{
D_SOCKET *dsock, *dsock_next;
for (dsock = dsock_list; dsock; dsock = dsock_next)
{
dsock_next = dsock->next;
if (dsock->lookup_status != TSTATE_CLOSED) continue;
/* remove the socket from the socket list */
if (dsock == dsock_list)
dsock_list = dsock->next;
else
{
D_SOCKET *prev;
for (prev = dsock_list; prev && prev->next != dsock; prev = prev->next)
;
if (prev)
prev->next = dsock->next;
else
bug("Recycle_sockets: Closed socket not in list");
}
/* free the memory */
free(dsock->hostname);
/* stop compression */
#ifndef NOMCCP
compressEnd(dsock, dsock->compressing, TRUE);
#endif
/* put the socket in the free_list */
dsock->next = dsock_free;
dsock_free = dsock;
}
}