mirror of
https://github.com/EnergyMech/energymech.git
synced 2025-12-29 16:14:43 +00:00
663 lines
13 KiB
C
663 lines
13 KiB
C
/*
|
|
|
|
EnergyMech, IRC bot software
|
|
Parts Copyright (c) 1997-2018 proton
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
#ifndef GENCMD_C
|
|
#define SOCKET_C
|
|
#include "config.h"
|
|
|
|
#include "defines.h"
|
|
#include "structs.h"
|
|
#include "global.h"
|
|
#include "h.h"
|
|
|
|
#ifndef RAWDNS
|
|
|
|
/*
|
|
* only include this hack if rawdns isnt compiled in
|
|
*/
|
|
uint32_t get_ip(const char *host)
|
|
{
|
|
struct hostent *he;
|
|
uint32_t ip;
|
|
|
|
if ((ip = inet_addr(host)) == INADDR_NONE)
|
|
{
|
|
if ((he = gethostbyname(host)) == NULL)
|
|
{
|
|
#ifdef DEBUG
|
|
debug("(get_ip) unable to resolve %s\n",host);
|
|
#endif /* DEBUG */
|
|
return(-1);
|
|
}
|
|
ip = (uint32_t)((struct in_addr*)he->h_addr)->s_addr;
|
|
}
|
|
#ifdef DEBUG
|
|
debug("(get_ip) %s -> %s\n",host,inet_ntoa(*((struct in_addr*)&ip)));
|
|
#endif /* DEBUG */
|
|
return(ip);
|
|
}
|
|
|
|
#endif /* ! RAWDNS */
|
|
|
|
/*
|
|
* some default code for socket flags
|
|
*/
|
|
void SockFlags(int fd)
|
|
{
|
|
#ifdef ASSUME_SOCKOPTS
|
|
fcntl(fd,F_SETFL,O_NONBLOCK|O_RDWR);
|
|
fcntl(fd,F_SETFD,FD_CLOEXEC);
|
|
#else /* not ASSUME_SOCKOPTS */
|
|
fcntl(fd,F_SETFL,O_NONBLOCK | fcntl(fd,F_GETFL));
|
|
fcntl(fd,F_SETFD,FD_CLOEXEC | fcntl(fd,F_GETFD));
|
|
#endif /* ASSUME_SOCKOPTS */
|
|
}
|
|
|
|
int SockOpts(void)
|
|
{
|
|
struct { int onoff; int linger; } parm;
|
|
int s;
|
|
|
|
if ((s = socket(AF_INET,SOCK_STREAM,0)) < 0)
|
|
return(-1);
|
|
|
|
parm.onoff = parm.linger = 0;
|
|
setsockopt(s,SOL_SOCKET,SO_LINGER,(char*)&parm,sizeof(parm));
|
|
parm.onoff++;
|
|
setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,(char*)&parm.onoff,sizeof(int));
|
|
|
|
SockFlags(s);
|
|
|
|
return(s);
|
|
}
|
|
|
|
int SockListener(int port)
|
|
{
|
|
struct sockaddr_in sai;
|
|
int s;
|
|
|
|
if ((s = SockOpts()) < 0)
|
|
return(-1);
|
|
memset((char*)&sai,0,sizeof(sai));
|
|
sai.sin_family = AF_INET;
|
|
sai.sin_addr.s_addr = INADDR_ANY;
|
|
sai.sin_port = htons(port);
|
|
if ((bind(s,(struct sockaddr*)&sai,sizeof(sai)) < 0) || (listen(s,1) < 0))
|
|
{
|
|
close(s);
|
|
return(-1);
|
|
}
|
|
return(s);
|
|
}
|
|
|
|
#ifdef RAWDNS
|
|
#define get_ip rawdns_get_ip
|
|
#endif /* RAWDNS */
|
|
|
|
int SockConnect(char *host, int port, int use_vhost)
|
|
{
|
|
struct sockaddr_in sai;
|
|
int s;
|
|
|
|
#ifdef DEBUG
|
|
debug("(SockConnect) %s %i%s\n",nullstr(host),port,(use_vhost) ? " [VHOST]" : "");
|
|
#endif /* DEBUG */
|
|
|
|
if ((s = SockOpts()) < 0)
|
|
return(-1);
|
|
|
|
memset((char*)&sai,0,sizeof(sai));
|
|
sai.sin_family = AF_INET;
|
|
|
|
if (use_vhost && ((current->vhost_type & VH_IPALIAS_FAIL) == 0)
|
|
&& current->setting[STR_VIRTUAL].str_var)
|
|
{
|
|
current->vhost_type |= VH_IPALIAS_BOTH;
|
|
if ((sai.sin_addr.s_addr = get_ip(current->setting[STR_VIRTUAL].str_var)) != -1)
|
|
{
|
|
if (bind(s,(struct sockaddr *)&sai,sizeof(sai)) >= 0)
|
|
{
|
|
current->vhost_type &= ~(VH_IPALIAS_FAIL|VH_WINGATE);
|
|
#ifdef WINGATE
|
|
use_vhost++;
|
|
#endif /* WINGATE */
|
|
#ifdef DEBUG
|
|
debug("(SockConnect) IP Alias virtual host bound OK\n");
|
|
#endif /* DEBUG */
|
|
}
|
|
}
|
|
}
|
|
|
|
memset((char*)&sai,0,sizeof(sai));
|
|
sai.sin_family = AF_INET;
|
|
|
|
#ifdef WINGATE
|
|
/*
|
|
* bouncer connects (WinGate, ...)
|
|
*/
|
|
if ((use_vhost == TRUE) && ((current->vhost_type & VH_WINGATE_FAIL) == 0)
|
|
&& current->setting[STR_WINGATE].str_var)
|
|
{
|
|
#ifdef DEBUG
|
|
debug("(SockConnect) Trying wingate @ %s:%i\n",
|
|
current->setting[STR_WINGATE].str_var,
|
|
current->setting[INT_WINGPORT].int_var);
|
|
#endif /* DEBUG */
|
|
current->vhost_type |= VH_WINGATE_BOTH;
|
|
sai.sin_port = htons(current->setting[INT_WINGPORT].int_var);
|
|
if ((sai.sin_addr.s_addr = get_ip(current->setting[STR_WINGATE].str_var)) == -1)
|
|
{
|
|
close(s);
|
|
return(-1);
|
|
}
|
|
current->vhost_type &= ~(VH_WINGATE_FAIL|VH_IPALIAS);
|
|
#ifdef DEBUG
|
|
debug("(SockConnect) WINGATE host resolved OK\n");
|
|
#endif /* DEBUG */
|
|
}
|
|
else
|
|
#endif /* WINGATE */
|
|
{
|
|
/*
|
|
* Normal connect, no bounces...
|
|
*/
|
|
sai.sin_port = htons(port);
|
|
if ((sai.sin_addr.s_addr = get_ip(host)) == -1)
|
|
{
|
|
close(s);
|
|
return(-1);
|
|
}
|
|
sai.sin_family = AF_INET;
|
|
}
|
|
|
|
if ((connect(s,(struct sockaddr*)&sai,sizeof(sai)) < 0) && (errno != EINPROGRESS))
|
|
{
|
|
#ifdef DEBUG
|
|
debug("[CbN] unable to connect. errno = %i\n",errno);
|
|
#endif /* DEBUG */
|
|
close(s);
|
|
return(-1);
|
|
}
|
|
#ifdef DEBUG
|
|
debug("(SockConnect) {%i} %s %i%s\n",s,nullstr(host),port,(use_vhost) ? " [VHOST]" : "");
|
|
#endif /* DEBUG */
|
|
return(s);
|
|
}
|
|
|
|
int SockAccept(int sock)
|
|
{
|
|
struct sockaddr_in sai;
|
|
int s,sz;
|
|
|
|
sz = sizeof(sai);
|
|
s = accept(sock,(struct sockaddr*)&sai,&sz);
|
|
if (s >= 0)
|
|
{
|
|
SockFlags(s);
|
|
}
|
|
return(s);
|
|
}
|
|
|
|
#endif /* GENCMD_C */
|
|
|
|
/*
|
|
* Format text and send to a socket or file descriptor
|
|
*/
|
|
int to_file(const int sock, const char *format, ...)
|
|
{
|
|
va_list msg;
|
|
#if defined(DEBUG) && !defined(GENCMD_C)
|
|
char *line,*rest;
|
|
int i;
|
|
#endif /* DEBUG */
|
|
|
|
if (sock == -1)
|
|
return(-1);
|
|
|
|
va_start(msg,format);
|
|
vsprintf(globaldata,format,msg);
|
|
va_end(msg);
|
|
|
|
#if defined(DEBUG) && !defined(GENCMD_C)
|
|
i = write(sock,globaldata,strlen(globaldata));
|
|
rest = globaldata;
|
|
while((line = get_token(&rest,"\n"))) /* rest cannot be NULL */
|
|
debug("(out) {%i} %s\n",sock,nullstr(line));
|
|
if (i < 0)
|
|
debug("(out) {%i} errno = %i\n",sock,errno);
|
|
return(i);
|
|
#else /* DEBUG */
|
|
return(write(sock,globaldata,strlen(globaldata)));
|
|
#endif /* DEBUG */
|
|
}
|
|
|
|
#ifndef GENCMD_C
|
|
|
|
/*
|
|
* Format a message and send it to the current bots server
|
|
* to_server needs a newline (\n) it wont manufacture it itself.
|
|
*/
|
|
void to_server(char *format, ...)
|
|
{
|
|
va_list msg;
|
|
#ifdef DEBUG
|
|
char *line,*rest;
|
|
#endif /* DEBUG */
|
|
|
|
if (current->sock == -1)
|
|
return;
|
|
|
|
va_start(msg,format);
|
|
vsprintf(globaldata,format,msg);
|
|
va_end(msg);
|
|
|
|
/*
|
|
* each line we send to the server pushes the sendq time
|
|
* forward *two* seconds
|
|
*/
|
|
current->sendq_time += 2;
|
|
|
|
if (write(current->sock,globaldata,strlen(globaldata)) < 0)
|
|
{
|
|
#ifdef DEBUG
|
|
debug("[StS] {%i} errno = %i\n",current->sock,errno);
|
|
#endif /* DEBUG */
|
|
close(current->sock);
|
|
current->sock = -1;
|
|
current->connect = CN_NOSOCK;
|
|
return;
|
|
}
|
|
#ifdef DEBUG
|
|
rest = globaldata;
|
|
while((line = get_token(&rest,"\n"))) /* rest cannot be NULL */
|
|
debug("[StS] {%i} %s\n",current->sock,line);
|
|
#endif /* DEBUG */
|
|
}
|
|
|
|
void to_user_q(const char *target, const char *format, ...)
|
|
{
|
|
va_list args;
|
|
Client *client;
|
|
Strp *new,**pp;
|
|
char message[MAXLEN];
|
|
char nick[MAXHOSTLEN];
|
|
char *fmt;
|
|
|
|
va_start(args,format);
|
|
vsprintf(message,format,args);
|
|
va_end(args);
|
|
|
|
stringcat(message,"\n");
|
|
|
|
if (STARTUP_ECHOTOCONSOLE)
|
|
{
|
|
int n;
|
|
n = write(1,message,strlen(message));
|
|
return;
|
|
}
|
|
|
|
fmt = "NOTICE %s :%s";
|
|
if (CurrentChan)
|
|
{
|
|
target = CurrentChan->name;
|
|
fmt = "PRIVMSG %s :%s";
|
|
}
|
|
|
|
if (!ischannel(target))
|
|
target = nickcpy(nick,target);
|
|
|
|
if ((client = find_client(target)))
|
|
{
|
|
if (write(client->sock,message,strlen(message)) < 0)
|
|
client->flags = DCC_DELETE;
|
|
#ifdef DEBUG
|
|
debug("(to_user) {%i} [%s] %s",client->sock,target,message);
|
|
#endif /* DEBUG */
|
|
return;
|
|
}
|
|
|
|
#ifdef REDIRECT
|
|
if (redirect.to && target == redirect.to)
|
|
{
|
|
send_redirect(message);
|
|
return;
|
|
}
|
|
#endif /* REDIRECT */
|
|
|
|
pp = ¤t->sendq;
|
|
while(*pp)
|
|
pp = &(*pp)->next;
|
|
|
|
set_mallocdoer(to_user_q);
|
|
*pp = new = (Strp*)Calloc(sizeof(Strp) + StrlenX(fmt,target,message,NULL));
|
|
/* Calloc sets to zero new->next = NULL; */
|
|
sprintf(new->p,fmt,target,message);
|
|
}
|
|
|
|
/*
|
|
* Format a message and send it either through DCC if the user is
|
|
* connected to the partyline, or send it as a NOTICE if he's not
|
|
*/
|
|
void to_user(const char *target, const char *format, ...)
|
|
{
|
|
va_list args;
|
|
Client *client;
|
|
char message[MAXLEN];
|
|
char *s;
|
|
|
|
#ifdef DEBUG
|
|
if (!*target)
|
|
return;
|
|
#endif /* DEBUG */
|
|
|
|
s = message;
|
|
|
|
#ifdef REDIRECT
|
|
|
|
if (redirect.to)
|
|
client = NULL;
|
|
else
|
|
client = find_client(target);
|
|
|
|
if (!redirect.to && !client)
|
|
|
|
#else /* REDIRECT */
|
|
|
|
if ((client = find_client(target)) == NULL)
|
|
|
|
#endif /* REDIRECT */
|
|
|
|
{
|
|
if (current->sock == -1)
|
|
{
|
|
#ifdef DEBUG
|
|
debug("(to_user) [%s] current->sock == -1\n",target);
|
|
#endif /* DEBUG */
|
|
return;
|
|
}
|
|
s = stringcpy(message,"NOTICE ");
|
|
if (ischannel(target))
|
|
stringcat(message,target);
|
|
else
|
|
nickcpy(s,target);
|
|
s = stringcat(message," :");
|
|
}
|
|
|
|
va_start(args,format);
|
|
vsprintf(s,format,args);
|
|
va_end(args);
|
|
|
|
#ifdef REDIRECT
|
|
if (redirect.to)
|
|
{
|
|
send_redirect(message);
|
|
return;
|
|
}
|
|
#endif /* REDIRECT */
|
|
|
|
/*
|
|
* tag on a newline for DCC or server
|
|
*/
|
|
s = stringcat(message,"\n");
|
|
|
|
#ifdef DEBUG
|
|
*s = 0;
|
|
#endif /* DEBUG */
|
|
|
|
if (client)
|
|
{
|
|
if (write(client->sock,message,(s - message)) < 0)
|
|
client->flags = DCC_DELETE;
|
|
#ifdef DEBUG
|
|
debug("(to_user) {%i} [%s] %s",client->sock,target,message);
|
|
#endif /* DEBUG */
|
|
return;
|
|
}
|
|
|
|
current->sendq_time += 2;
|
|
|
|
if (write(current->sock,message,(s - message)) < 0)
|
|
{
|
|
#ifdef DEBUG
|
|
debug("(to_user) {%i} [%s] errno = %i\n",current->sock,target,errno);
|
|
#endif /* DEBUG */
|
|
close(current->sock);
|
|
current->sock = -1;
|
|
current->connect = CN_NOSOCK;
|
|
return;
|
|
}
|
|
#ifdef DEBUG
|
|
debug("(to_user) {%i} [%s] %s",current->sock,target,message);
|
|
#endif /* DEBUG */
|
|
}
|
|
|
|
#endif /* ifndef GENCMD_C */
|
|
|
|
/*
|
|
* Read any data waiting on a socket or file descriptor
|
|
* and return any complete lines to the caller
|
|
*
|
|
* 1: Try to get a whole line from <rest> data
|
|
* 2: If <rest> data is insufficient, try to read in more
|
|
* 3: Try again to make a whole line
|
|
*/
|
|
char *sockread(int socket, char *rest, char *output)
|
|
{
|
|
char *src,*dst,*rdst;
|
|
int n;
|
|
|
|
errno = EAGAIN;
|
|
|
|
src = rest;
|
|
dst = output;
|
|
|
|
while(*src)
|
|
{
|
|
if (*src == '\n' || *src == '\r')
|
|
{
|
|
gotline:
|
|
while(*src == '\n' || *src == '\r')
|
|
src++;
|
|
*dst = 0;
|
|
#if !defined(GENCMD_C)
|
|
cx.rest_end = dst;
|
|
#endif /* !defined(GENCMD_C) */
|
|
|
|
/* move remainder of rest to the beginning of the buffer */
|
|
/* src can be end of rest or globaldata */
|
|
dst = rest;
|
|
while(*src)
|
|
*(dst++) = *(src++);
|
|
*dst = 0;
|
|
|
|
#if defined(DEBUG) && !defined(GENCMD_C)
|
|
debug("(in) {%i} %s\n",socket,output);
|
|
#endif /* DEBUG */
|
|
return((*output) ? output : NULL);
|
|
}
|
|
*(dst++) = *(src++);
|
|
}
|
|
|
|
n = read(socket,globaldata,MSGLEN-2);
|
|
switch(n)
|
|
{
|
|
case 0:
|
|
errno = EPIPE;
|
|
case -1:
|
|
return(NULL);
|
|
}
|
|
|
|
rdst = src;
|
|
globaldata[n] = 0;
|
|
src = globaldata;
|
|
|
|
while(*src)
|
|
{
|
|
if (*src == '\r' || *src == '\n')
|
|
goto gotline; /* gotline will move the rest of globaldata to rest */
|
|
if ((dst - output) >= (MSGLEN-2))
|
|
{
|
|
/*
|
|
* line is longer than buffer, let the wheel spin
|
|
*/
|
|
src++;
|
|
continue;
|
|
}
|
|
*(rdst++) = *(dst++) = *(src++);
|
|
}
|
|
*rdst = 0;
|
|
return(NULL);
|
|
}
|
|
|
|
void readline(int fd, int (*callback)(char *))
|
|
{
|
|
char linebuf[MSGLEN],readbuf[MSGLEN];
|
|
char *ptr;
|
|
int oc;
|
|
|
|
*readbuf = 0;
|
|
|
|
do
|
|
{
|
|
ptr = sockread(fd,readbuf,linebuf);
|
|
oc = errno;
|
|
if (ptr && *ptr)
|
|
{
|
|
if (callback(ptr) == TRUE)
|
|
oc = EPIPE;
|
|
}
|
|
}
|
|
while(oc == EAGAIN);
|
|
|
|
close(fd);
|
|
|
|
#if defined(DEBUG) && !defined(GENCMD_C)
|
|
debug("(readline) done reading lines\n");
|
|
#endif /* DEBUG */
|
|
}
|
|
|
|
#ifndef GENCMD_C
|
|
|
|
void remove_ks(KillSock *ks)
|
|
{
|
|
KillSock *ksp;
|
|
|
|
close(ks->sock);
|
|
if (ks == killsocks)
|
|
{
|
|
killsocks = ks->next;
|
|
}
|
|
else
|
|
{
|
|
ksp = killsocks;
|
|
while(ksp && (ksp->next != ks))
|
|
ksp = ksp->next;
|
|
if (ksp)
|
|
{
|
|
ksp->next = ks->next;
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
debug("(killsock) {%i} removing killsocket\n",ks->sock);
|
|
#endif /* DEBUG */
|
|
Free((char**)&ks);
|
|
}
|
|
|
|
int killsock(int sock)
|
|
{
|
|
KillSock *ks,*ksnext;
|
|
struct timeval t;
|
|
fd_set rd,wd;
|
|
char bitbucket[MSGLEN];
|
|
int hi,n;
|
|
|
|
if (sock >= 0)
|
|
{
|
|
set_mallocdoer(killsock);
|
|
ks = (KillSock*)Calloc(sizeof(KillSock));
|
|
ks->time = now;
|
|
ks->sock = sock;
|
|
ks->next = killsocks;
|
|
killsocks = ks;
|
|
#ifdef DEBUG
|
|
debug("(killsock) {%i} added killsocket\n",ks->sock);
|
|
#endif /* DEBUG */
|
|
return(TRUE);
|
|
}
|
|
|
|
if (killsocks == NULL)
|
|
return(FALSE);
|
|
|
|
if (sock == -1)
|
|
t.tv_sec = 0;
|
|
else
|
|
t.tv_sec = 1;
|
|
t.tv_usec = 0;
|
|
|
|
FD_ZERO(&rd);
|
|
FD_ZERO(&wd);
|
|
|
|
hi = -1;
|
|
for(ks=killsocks;ks;ks=ks->next)
|
|
{
|
|
if (ks->sock > hi)
|
|
hi = ks->sock;
|
|
FD_SET(ks->sock,&rd);
|
|
FD_SET(ks->sock,&wd);
|
|
}
|
|
|
|
if (select(hi+1,&rd,&wd,NULL,&t) == -1)
|
|
{
|
|
switch(errno)
|
|
{
|
|
case EINTR:
|
|
/* have to redo the select */
|
|
case ENOMEM:
|
|
/* should never happen, but still */
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
for(ks=killsocks;ks;)
|
|
{
|
|
ksnext = ks->next;
|
|
if (FD_ISSET(ks->sock,&rd))
|
|
{
|
|
n = read(ks->sock,&bitbucket,MSGLEN);
|
|
if ((n == 0) || ((n == -1) && (errno != EAGAIN)))
|
|
remove_ks(ks);
|
|
}
|
|
if ((now - ks->time) > KILLSOCKTIMEOUT)
|
|
remove_ks(ks);
|
|
ks = ksnext;
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
void do_clearqueue(COMMAND_ARGS)
|
|
{
|
|
#ifdef DEBUG
|
|
debug("(do_clearqueue) purging sendq...\n");
|
|
#endif
|
|
purge_linklist((void**)¤t->sendq);
|
|
}
|
|
|
|
#endif /* GENCMD_C */
|