1
0
mirror of https://github.com/unrealircd/unrealircd.git synced 2026-06-25 03:16:38 +02:00
Files
unrealircd/src/modules/scan_socks.c
T
2002-12-09 20:13:51 +00:00

508 lines
13 KiB
C

/*
* IRC - Internet Relay Chat, src/modules/scan_socks.c
* (C) 2001 Carsten Munk (Techie/Stskeeps) <stskeeps@tspre.org>
*
* SOCKS4/5 scanning module for scan.so
*
* See file AUTHORS in IRC package for additional names of
* the programmers.
*
* 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 1, 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.
*/
#include "config.h"
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "channel.h"
#include <time.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <io.h>
#else
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#endif
#include <fcntl.h>
#include "h.h"
#ifdef STRIPBADWORDS
#include "badwords.h"
#endif
#ifdef _WIN32
#include "version.h"
#endif
#include "modules/scan.h"
#ifndef SCAN_ON_PORT
#define SCAN_ON_PORT 1080
#endif
static Hook *SocksScanHost = NULL;
static vFP xEadd_scan = NULL;
static struct SOCKADDR_IN *xScan_endpoint = NULL;
static struct IN_ADDR *xScan_bind = NULL;
static int *xScan_TimeOut = 0;
#ifdef STATIC_LINKING
extern void Eadd_scan();
extern struct SOCKADDR_IN Scan_endpoint;
extern int Scan_TimeOut;
extern struct IN_ADDR Scan_bind;
#endif
void scan_socks_scan(Scan_AddrStruct *sr);
void scan_socks4_scan(Scan_AddrStruct *sr);
void scan_socks5_scan(Scan_AddrStruct *sr);
static int HOOKTYPE_SCAN_HOST;
static Hooktype *ScanHost;
static Mod_SymbolDepTable modsymdep[] =
{
MOD_Dep(Eadd_scan, xEadd_scan, "src/modules/scan.so"),
MOD_Dep(Scan_endpoint, xScan_endpoint, "src/modules/scan.so"),
MOD_Dep(Scan_bind, xScan_bind, "src/modules/scan.so"),
MOD_Dep(Scan_TimeOut, xScan_TimeOut, "src/modules/scan.so"),
{NULL, NULL}
};
ModuleInfo ScanSocksModInfo;
#ifndef DYNAMIC_LINKING
ModuleHeader scan_socks_Header
#else
ModuleHeader Mod_Header
#endif
= {
"scan_socks", /* Name of module */
"$Id$", /* Version */
"scanning API: socks", /* Short description of module */
"3.2-b8-1",
modsymdep
};
/*
* The purpose of these ifdefs, are that we can "static" link the ircd if we
* want to
*/
/* This is called on module init, before Server Ready */
#ifdef DYNAMIC_LINKING
DLLFUNC int Mod_Init(ModuleInfo *modinfo)
#else
int scan_socks_Init(ModuleInfo *modinfo)
#endif
{
/*
* Add scanning hooks
*/
bcopy(modinfo,&ScanSocksModInfo,modinfo->size);
ScanHost = HooktypeAdd(ScanSocksModInfo.handle, "HOOKTYPE_SCAN_HOST", &HOOKTYPE_SCAN_HOST);
SocksScanHost = HookAddVoidEx(ScanSocksModInfo.handle, HOOKTYPE_SCAN_HOST, scan_socks_scan);
return MOD_SUCCESS;
}
/* Is first run when server is 100% ready */
#ifdef DYNAMIC_LINKING
DLLFUNC int Mod_Load(int module_load)
#else
int scan_socks_Load(int module_load)
#endif
{
return MOD_SUCCESS;
}
/* Called when module is unloaded */
#ifdef DYNAMIC_LINKING
DLLFUNC int Mod_Unload(int module_unload)
#else
int scan_socks_Unload(int module_unload)
#endif
{
HookDel(SocksScanHost);
HooktypeDel(ScanHost,ScanSocksModInfo.handle);
return MOD_SUCCESS;
}
#define HICHAR(s) (((unsigned short) s) >> 8)
#define LOCHAR(s) (((unsigned short) s) & 0xFF)
void scan_socks_scan(Scan_AddrStruct *h)
{
THREAD thread[2];
unsigned id[2];
u_int32_t dwRc[2];
IRCMutexLock((h->lock));
h->refcnt++;
IRCCreateThreadEx(thread[0], scan_socks4_scan, h, &id[0]);
h->refcnt++;
IRCCreateThreadEx(thread[1], scan_socks5_scan, h, &id[1]);
IRCMutexUnlock((h->lock));
IRCJoinThread(thread[0], &dwRc[0]);
IRCJoinThread(thread[1], &dwRc[1]);
IRCMutexLock((h->lock));
h->refcnt--;
IRCMutexUnlock((h->lock));
IRCDetachThread(IRCThreadSelf());
return;
}
void scan_socks4_scan(Scan_AddrStruct *h)
{
int retval;
#ifdef INET6
unsigned char *cp;
#endif
struct SOCKADDR_IN sin;
struct SOCKADDR_IN bin;
struct in_addr ia4;
SOCKET fd;
unsigned char socksbuf[10];
unsigned long theip;
fd_set rfds, efds;
struct timeval tv;
int err, len = sizeof(err);
/* Get host */
IRCMutexLock((h->lock));
#ifndef INET6
sin.SIN_ADDR.S_ADDR = h->in.S_ADDR;
#else
bcopy(sin.SIN_ADDR.S_ADDR, h->in.S_ADDR, sizeof(h->in.S_ADDR));
#endif
IRCMutexUnlock((h->lock));
#ifdef INET6
/* ::ffff:ip hack */
cp = (u_char *)&h->in.s6_addr;
if (!(cp[0] == 0 && cp[1] == 0 && cp[2] == 0 && cp[3] == 0 && cp[4] == 0
&& cp[5] == 0 && cp[6] == 0 && cp[7] == 0 && cp[8] == 0
&& cp[9] == 0 && ((cp[10] == 0 && cp[11] == 0) || (cp[10] == 0xff
&& cp[11] == 0xff))))
goto exituniverse;
#endif
if ((fd = socket(AFINET, SOCK_STREAM, 0)) < 0)
{
goto exituniverse;
return;
}
#ifndef INET6
bin.SIN_ADDR = *xScan_bind;
#else
bcopy((char *)xScan_bind, (char *)&bin.SIN_ADDR, sizeof(struct IN_ADDR));
#endif
bin.SIN_FAMILY = AFINET;
bin.SIN_PORT = 0;
bind(fd, (struct SOCKADDR *)&bin, sizeof(bin));
sin.SIN_PORT = htons((unsigned short)SCAN_ON_PORT);
sin.SIN_FAMILY = AFINET;
/* We do this non-blocking to prevent a hang of the entire ircd with newer
* versions of glibc. Don't you just love new "features?"
* Passing null to this is probably bad, a better method is needed.
* Maybe a version of set_non_blocking that doesn't send error messages?
* -Zogg
*
* set_non_blocking(fd,cptr)
* when cptr == NULL, it doesnt error - changed some months ago
* also, don't we need a select loop to make this better?
* -Stskeeps
* I just gave a select loop a shot (select in a while(), waiting for
* the thing to either set the writable flags or return a -# and set
* errno to EINTR. Could be my ignorance, or my glibc, but select()
* NEVER returned a negative number, and if I passed it a timeout (ie, tv)
* then the loops never ended, either.
* -Zogg
*/
set_non_blocking(fd, NULL);
if ((retval = connect(fd, (struct sockaddr *)&sin, sizeof(sin))) == -1 && !(ERRNO == P_EWORKING))
{
/* we have no socks server! */
CLOSE_SOCK(fd);
goto exituniverse;
return;
}
/* We wait for connection to complete */
tv.tv_sec = *xScan_TimeOut;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
FD_ZERO(&efds);
FD_SET(fd, &efds);
if (select(fd + 1, NULL, &rfds, &efds, &tv) <= 0)
{
CLOSE_SOCK(fd);
goto exituniverse;
}
/* did connection fail on windows? */
if (FD_ISSET(fd, &efds))
{
err = ERRNO;
CLOSE_SOCK(fd);
goto exituniverse;
}
#ifdef SO_ERROR
/* did connection fail on unix? */
if (!getsockopt(fd, SOL_SOCKET, SO_ERROR, (OPT_TYPE *)&err, &len))
if (err)
{
/* connection failed */
CLOSE_SOCK(fd);
goto exituniverse;
}
#endif
#ifdef INET6
ia4.s_addr = inet_aton((char *)Inet_ia2p(&xScan_endpoint->SIN_ADDR));
#else
bcopy(&xScan_endpoint->sin_addr, &ia4, sizeof(struct IN_ADDR));
#endif
theip = htonl(ia4.s_addr);
bzero(socksbuf, sizeof(socksbuf));
socksbuf[0] = 4;
socksbuf[1] = 1;
socksbuf[2] = LOCHAR(xScan_endpoint->SIN_PORT);
socksbuf[3] = HICHAR(xScan_endpoint->SIN_PORT);
socksbuf[4] = (theip >> 24);
socksbuf[5] = (theip >> 16) & 0xFF;
socksbuf[6] = (theip >> 8) & 0xFF;
socksbuf[7] = theip & 0xFF;
if ((retval = send(fd, socksbuf, 9, 0)) != 9)
{
CLOSE_SOCK(fd);
goto exituniverse;
}
/* Now we wait for data. 10 secs ought to be enough */
tv.tv_sec = *xScan_TimeOut;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
if ((retval = select(fd + 1, &rfds, NULL, NULL, &tv)))
{
/* There's data in the jar. Let's read it */
len = recv(fd, socksbuf, 9, 0);
CLOSE_SOCK(fd);
if (len < 4)
{
goto exituniverse;
}
if (socksbuf[1] == 90)
{
/* We found SOCKS. */
IRCMutexLock((h->lock));
(*xEadd_scan)(&h->in, "Open SOCKS4 server");
IRCMutexUnlock((h->lock));
goto exituniverse;
}
}
else
{
CLOSE_SOCK(fd);
}
exituniverse:
IRCMutexLock((h->lock));
h->refcnt--;
IRCMutexUnlock((h->lock));
/* We get joined, we need no steekin Detach */
return;
}
void scan_socks5_scan(Scan_AddrStruct *h)
{
int retval;
#ifdef INET6
unsigned char *cp;
#endif
struct SOCKADDR_IN sin;
struct in_addr ia4;
SOCKET fd;
unsigned long theip;
fd_set rfds, efds;
struct timeval tv;
int err, len = sizeof(err);
unsigned char socksbuf[10];
/* Get host */
IRCMutexLock((h->lock));
#ifndef INET6
sin.SIN_ADDR.S_ADDR = h->in.S_ADDR;
#else
bcopy(sin.SIN_ADDR.S_ADDR, h->in.S_ADDR, sizeof(h->in.S_ADDR));
#endif
IRCMutexUnlock((h->lock));
/* IPv6 ?*/
#ifdef INET6
/* ::ffff:ip hack */
cp = (u_char *)&h->in.s6_addr;
if (!(cp[0] == 0 && cp[1] == 0 && cp[2] == 0 && cp[3] == 0 && cp[4] == 0
&& cp[5] == 0 && cp[6] == 0 && cp[7] == 0 && cp[8] == 0
&& cp[9] == 0 && ((cp[10] == 0 && cp[11] == 0) || (cp[10] == 0xff
&& cp[11] == 0xff))))
goto exituniverse;
#endif
if ((fd = socket(AFINET, SOCK_STREAM, 0)) < 0)
{
goto exituniverse;
return;
}
sin.SIN_PORT = htons((unsigned short)SCAN_ON_PORT);
sin.SIN_FAMILY = AFINET;
/* We do this non-blocking to prevent a hang of the entire ircd with newer
* versions of glibc. Don't you just love new "features?"
* Passing null to this is probably bad, a better method is needed.
* Maybe a version of set_non_blocking that doesn't send error messages?
* -Zogg
*
* set_non_blocking(fd,cptr)
* when cptr == NULL, it doesnt error - changed some months ago
* also, don't we need a select loop to make this better?
* -Stskeeps
* I just gave a select loop a shot (select in a while(), waiting for
* the thing to either set the writable flags or return a -# and set
* errno to EINTR. Could be my ignorance, or my glibc, but select()
* NEVER returned a negative number, and if I passed it a timeout (ie, tv)
* then the loops never ended, either.
* -Zogg
*/
set_non_blocking(fd, NULL);
if ((retval = connect(fd, (struct sockaddr *)&sin, sizeof(sin))) == -1 && !(ERRNO == P_EWORKING))
{
/* we have no socks server! */
CLOSE_SOCK(fd);
goto exituniverse;
}
/* We wait for connection to complete */
tv.tv_sec = *xScan_TimeOut;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
FD_ZERO(&efds);
FD_SET(fd, &efds);
if (select(fd + 1, NULL, &rfds, &efds, &tv) <= 0)
{
CLOSE_SOCK(fd);
goto exituniverse;
}
/* did connection fail on windows? */
if (FD_ISSET(fd, &efds))
{
err = ERRNO;
CLOSE_SOCK(fd);
goto exituniverse;
}
#ifdef SO_ERROR
/* did connection fail on unix? */
if (!getsockopt(fd, SOL_SOCKET, SO_ERROR, (OPT_TYPE *)&err, &len))
if (err)
{
/* connection failed */
CLOSE_SOCK(fd);
goto exituniverse;
}
#endif
#ifdef INET6
ia4.s_addr = inet_aton((char *)Inet_ia2p(&xScan_endpoint->SIN_ADDR));
#else
bcopy(&xScan_endpoint->sin_addr, &ia4, sizeof(struct IN_ADDR));
#endif
theip = htonl(ia4.s_addr);
bzero(socksbuf, sizeof(socksbuf));
if (send(fd, "\5\1\0", 3, 0) != 3) {
CLOSE_SOCK(fd);
goto exituniverse;
}
tv.tv_sec = *xScan_TimeOut;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
if ((retval = select(fd + 1, &rfds, NULL, NULL, &tv)))
{
/* There's data in the jar. Let's read it */
len = recv(fd, socksbuf, 2, 0);
if (len != 2)
{
CLOSE_SOCK(fd);
goto exituniverse;
}
if (socksbuf[0] != 5 || socksbuf[1] != 0) {
CLOSE_SOCK(fd);
goto exituniverse;
}
}
socksbuf[0] = 5;
socksbuf[1] = 1;
socksbuf[2] = 0;
socksbuf[3] = 1;
socksbuf[4] = (theip >> 24) & 0xFF;
socksbuf[5] = (theip >> 16) & 0xFF;
socksbuf[6] = (theip >> 8) & 0xFF;
socksbuf[7] = theip & 0xFF;
socksbuf[8] = HICHAR(xScan_endpoint->SIN_PORT);
socksbuf[9] = LOCHAR(xScan_endpoint->SIN_PORT);
if ((retval = send(fd, socksbuf, 10, 0)) != 10)
{
CLOSE_SOCK(fd);
goto exituniverse;
}
bzero(socksbuf, sizeof(socksbuf));
/* Now we wait for data. 10 secs ought to be enough */
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
if ((retval = select(fd + 1, &rfds, NULL, NULL, &tv)))
{
/* There's data in the jar. Let's read it */
len = recv(fd, socksbuf, 2, 0);
CLOSE_SOCK(fd);
if (len != 2)
{
goto exituniverse;
}
if (socksbuf[1] == 5 && socksbuf[1] == 0)
{
/* We found SOCKS. */
IRCMutexLock((h->lock));
(*xEadd_scan)(&h->in, "Open SOCKS5 server");
IRCMutexUnlock((h->lock));
goto exituniverse;
}
}
else
{
CLOSE_SOCK(fd);
}
exituniverse:
IRCMutexLock(h->lock);
h->refcnt--;
IRCMutexUnlock(h->lock);
/* We need no steekin detach */
return;
}