/*
tuved - Socket Server
(c) 2007 Christopher Olsen
$Id: socket.c,v 1.30 2008/02/05 01:27:29 reddawg Exp $
*/
#include <stdarg.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include "tuved.h"
myConnections_t *connections = 0x0;
int listenerFD = 0x0;
int highSock = 0x0;
int sStartListener() {
int optVal = 0x1;
struct sockaddr_in myAddr; // my address information
if ((listenerFD = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
if (setsockopt(listenerFD, SOL_SOCKET, SO_REUSEADDR, &optVal, sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
myAddr.sin_family = AF_INET; // host byte order
myAddr.sin_port = htons(MYPORT); // short, network byte order
myAddr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
memset(myAddr.sin_zero, '\0', sizeof myAddr.sin_zero);
if (bind(listenerFD, (struct sockaddr *)&myAddr, sizeof(struct sockaddr)) == -1) {
perror("bind");
exit(1);
}
if (listen(listenerFD, BACKLOG) == -1) {
perror("listen");
exit(1);
}
fcntl(listenerFD, F_SETFL, fcntl(listenerFD, F_GETFL, 0) | O_NONBLOCK);
sAddConnection(listenerFD,"127.0.0.1");
writeLog(2,"Listener FD: [%i]\n",listenerFD);
return(0x0);
}
ssize_t sReadSocket(int socketFD,void *buffer,size_t length) {
ssize_t recLen = 0x0;
recLen = read(socketFD,buffer,length);
if (recLen <= 2)
recLen = 0x0;
return(recLen);
}
int sGetConnection() {
int newFD = 0x0; // New Socket;
socklen_t sin_size;
struct sockaddr_in remoteAddr;
struct hostent *hp;
sin_size = sizeof(struct sockaddr_in);
if ((newFD = accept(listenerFD, (struct sockaddr *)&remoteAddr, &sin_size)) == -1) {
perror("accept");
}
/* Add Socket */
hp = gethostbyaddr((char *)&remoteAddr.sin_addr,sizeof(remoteAddr.sin_addr),AF_INET);
sAddConnection(newFD,(hp ? hp->h_name : inet_ntoa(remoteAddr.sin_addr)));
/* Return */
return(0x0);
}
int sAddConnection(int socketFD,char *host) {
myConnections_t *tmpConnection = 0x0;
writeLog(2,"Adding Socket: [%i]\n",socketFD);
userCount++;
if (connections == 0x0) {
connections = (myConnections_t *)calloc(1,sizeof(myConnections_t));
connections->prev = 0x0;
connections->next = 0x0;
connections->fd = socketFD;
connections->signedOn = time(NULL);
sprintf(connections->host,host);
}
else {
tmpConnection = (myConnections_t *)calloc(1,sizeof(myConnections_t));
//memset(tmpConnection,0x0,sizeof(myConnections_t));
tmpConnection->fd = socketFD;
tmpConnection->prev = connections;
tmpConnection->next = connections->next;
tmpConnection->signedOn = time(NULL);
sprintf(tmpConnection->host,host);
if (connections->next != 0x0)
connections->next->prev = tmpConnection;
connections->next = tmpConnection;
}
writeLog(2,"Socket Added\n");
return(0x0);
}
myConnections_t *sFindConnection(int fd) {
myConnections_t *tmpConnection = 0x0;
for (tmpConnection = connections->next;tmpConnection != 0x0;tmpConnection = tmpConnection->next) {
if (tmpConnection->fd == fd)
return(tmpConnection);
}
return(0x0);
}
int sCleanConnections() {
myConnections_t *tmpConnection = 0x0;
myConnections_t *delConnection = 0x0;
char output[256];
for (tmpConnection = connections->next;tmpConnection != 0x0;tmpConnection = tmpConnection->next) {
if (tmpConnection->userInfo.status == -1) {
// Set temporary pointer to connection so we can free it later
delConnection = tmpConnection;
// Hard close the socket so it doesn't linger
close(tmpConnection->fd);
writeLog(2,"Removed User: [%s]\n",tmpConnection->userInfo.username);
// If connection has a previous pointer which it always should adjust it's next pointer to match current next
if (tmpConnection->prev != 0x0)
tmpConnection->prev->next = tmpConnection->next;
// If connection has a next pointer which it may not adjust it to point to match our current previous
if (tmpConnection->next != 0x0)
tmpConnection->next->prev = tmpConnection->prev;
// Adjust tmpConnection to match our previous pointer which should always happen
if (tmpConnection->prev != 0x0)
tmpConnection = tmpConnection->prev;
else
tmpConnection = tmpConnection->next;
userCount--;
if (delConnection->userInfo.ident == 2) {
sprintf(output,"DELETE FROM active WHERE userid = %lld",delConnection->userInfo.uid);
dbQuery(output,0);
}
//Free the bad connection
free(delConnection);
writeLog(2,"Remove Completed\n");
}
}
return(0x0);
}
int sRemoveConnection(int socketFD) {
myConnections_t *tmpConnection = 0x0;
char output[256];
writeLog(2,"Removing Socket: %i\n",socketFD);
for (tmpConnection = connections->next;tmpConnection != 0x0;tmpConnection = tmpConnection->next) {
if (tmpConnection->fd == socketFD) {
tmpConnection->prev->next = tmpConnection->next;
if (tmpConnection->next != 0x0)
tmpConnection->next->prev = tmpConnection->prev;
writeLog(2,"Removed Socket: [%i]\n",tmpConnection->fd);
userCount--;
if (tmpConnection->userInfo.ident == 2) {
sprintf(output,"DELETE FROM active WHERE userid = %lld",tmpConnection->userInfo.uid);
dbQuery(output,0);
}
free(tmpConnection);
return(0x0);
}
}
writeLog(2,"Error: No Socket Removed\n");
return(-1);
}
int sGetConnections(fd_set *readset) {
int retVal = 0;
myConnections_t *tmpConnection = 0x0;
FD_ZERO(readset);
// FD_SET(listenerFD,readset);
highSock = 0;
if (connections != 0x0) {
for (tmpConnection = connections;tmpConnection != 0x0;tmpConnection = tmpConnection->next) {
FD_SET(tmpConnection->fd,readset);
if (tmpConnection->fd > highSock)
highSock = tmpConnection->fd;
}
retVal = 1;
}
return(retVal);
}
/************************************
* *
* Send ping broadcast to all users *
* *
************************************/
int sSendPing(time_t ping) {
myConnections_t *tmpConnection = 0x0;
char sPing[32];
for (tmpConnection = connections->next;tmpConnection != 0x0;tmpConnection = tmpConnection->next) {
if (tmpConnection->userInfo.pfailed < MAX_PING) {
if (tmpConnection->userInfo.pong < ping) {
tmpConnection->userInfo.pfailed++;
tmpConnection->userInfo.pong = ping;
if (tmpConnection->userInfo.ident >= 0x1) {
sprintf(sPing,"PING %i\r\n", time(NULL));
send(tmpConnection->fd,sPing,strlen(sPing),MSG_NOSIGNAL);
}
}
}
else {
send(tmpConnection->fd,"TIMEOUT\r\n",sizeof("TIMEOUT\r\n"),MSG_NOSIGNAL);
tuveDelUserChans(tmpConnection,"TIMEOUT");
tmpConnection->userInfo.status = -1;
}
}
return(0x0);
} /* End sSendPing() */
int sSendData(myConnections_t *con,char const * __restrict fmt, ...) {
int len = 0;
char data[2048];
va_list ap;
if (con == 0x0) {
writeLog(1,"Error Sending Data");
return(0x1);
}
else if (con->userInfo.status == -1) {
writeLog(3,"User: %s Socket Not Avail\n",con->userInfo.username);
return(0x1);
}
va_start(ap,fmt);
len = vsnprintf(data,2046,fmt,ap);
va_end(ap);
//data[len++] = '\r';
data[len++] = '\n';
send(con->fd,data,len,MSG_NOSIGNAL);
return(0x0);
}
int sProcessConnections(fd_set *readset) {
myConnections_t *tmpConnection = 0x0;
for (tmpConnection = connections;tmpConnection != 0x0;tmpConnection = tmpConnection->next) {
if (FD_ISSET(tmpConnection->fd,readset) != 0x0) {
if (tmpConnection->fd == listenerFD) {
sGetConnection();
writeLog(2,"Listen Socket Was Ready?\n");
}
else {
if (tuveGetData(tmpConnection) == -1) {
close(tmpConnection->fd);
tuveDelUserChans(tmpConnection,"SIG PIPE");
sRemoveConnection(tmpConnection->fd);
}
}
}
}
return(0x0);
}