265 lines
7.6 KiB
C++
265 lines
7.6 KiB
C++
|
/**
|
||
|
* hermes antispam proxy
|
||
|
* Copyright (C) 2006, 2007 Juan José Gutiérrez de Quevedo <juanjo@gutierrezdequevedo.com>
|
||
|
*
|
||
|
* 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; version 2 of the License
|
||
|
*
|
||
|
* 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.,
|
||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||
|
*
|
||
|
* @author Juan José Gutiérrez de Quevedo <juanjo@gutierrezdequevedo.com>
|
||
|
*/
|
||
|
#include <string>
|
||
|
#include <windows.h>
|
||
|
|
||
|
using namespace std;
|
||
|
|
||
|
#define SERVICE_NAME "hermes anti-spam proxy"
|
||
|
#define SERVICE_SHORT_NAME "hermes"
|
||
|
#define SERVICE_DESCRIPTION_TEXT "An anti-spam proxy using a combination of techniques like greylisting, dnsbl/dnswl, SPF, etc."
|
||
|
|
||
|
//macros
|
||
|
#define ChangeServiceStatus(x,y,z) y.dwCurrentState=z; SetServiceStatus(x,&y);
|
||
|
#define MIN(x,y) (((x)<(y))?(x):(y))
|
||
|
#define cmp(x,y) strncmp(x,y,strlen(y))
|
||
|
#define msgbox(x,y,z) MessageBox(NULL,x,z SERVICE_NAME,MB_OK|y)
|
||
|
#define winerror(x) msgbox(x,MB_ICONERROR,"Error from ")
|
||
|
#define winmessage(x) msgbox(x,MB_ICONINFORMATION,"Message from ")
|
||
|
#define condfree(x) if(NULL!=x) free(x);
|
||
|
#define safemalloc(x,y,z) do { if(NULL==(x=(z)malloc(y))) { winerror("Error reserving memory"); exit(-1); } memset(x,0,y); } while(0)
|
||
|
#define _(x) x
|
||
|
|
||
|
/**
|
||
|
* The docs on microsoft's web don't seem very clear, so I have
|
||
|
* looked at the stunnel source code to understand how this thing
|
||
|
* works. What you see here is still original source, but is
|
||
|
* "inspired" by stunnel's source code (gui.c mainly).
|
||
|
* It's the real minimum needed to install, start and stop services
|
||
|
*/
|
||
|
|
||
|
extern bool quit;
|
||
|
|
||
|
extern int hermes_main(int,char**);
|
||
|
|
||
|
SERVICE_STATUS service_status;
|
||
|
SERVICE_STATUS_HANDLE service_status_handle;
|
||
|
|
||
|
int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int);
|
||
|
static void WINAPI service_main(DWORD,LPTSTR*);
|
||
|
static void WINAPI handler(DWORD);
|
||
|
static int service_install();
|
||
|
static int service_uninstall();
|
||
|
|
||
|
char **params=NULL;
|
||
|
#define free_params() \
|
||
|
do \
|
||
|
{ \
|
||
|
if(NULL!=params) \
|
||
|
{ \
|
||
|
condfree(params[0]); \
|
||
|
condfree(params[1]); \
|
||
|
} \
|
||
|
condfree(params); \
|
||
|
} \
|
||
|
while(0)
|
||
|
|
||
|
#define init_params() \
|
||
|
do \
|
||
|
{ \
|
||
|
free_params(); \
|
||
|
safemalloc(params,sizeof(char *)*2,char **); \
|
||
|
safemalloc(params[0],1*sizeof(char),char *); \
|
||
|
params[0][0]='\0'; \
|
||
|
safemalloc(params[1],1024*sizeof(char),char *); \
|
||
|
} \
|
||
|
while(0)
|
||
|
|
||
|
int WINAPI WinMain(HINSTANCE instance,HINSTANCE previous_instance,LPSTR cmdline,int cmdshow)
|
||
|
{
|
||
|
if(!cmp(cmdline,"-service"))
|
||
|
{
|
||
|
SERVICE_TABLE_ENTRY service_table[]={
|
||
|
{SERVICE_SHORT_NAME,service_main},
|
||
|
{NULL,NULL}
|
||
|
};
|
||
|
|
||
|
if(0==StartServiceCtrlDispatcher(service_table))
|
||
|
{
|
||
|
winerror("Error starting service dispatcher.");
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
else if(!cmp(cmdline,"-install"))
|
||
|
service_install();
|
||
|
else if(!cmp(cmdline,"-uninstall"))
|
||
|
service_uninstall();
|
||
|
else
|
||
|
{
|
||
|
//we know that hermes can only have one parameter, so
|
||
|
//just copy it
|
||
|
init_params();
|
||
|
strncpy(params[1],cmdline,1024);
|
||
|
hermes_main(2,(char **)params);
|
||
|
free_params();
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int service_install()
|
||
|
{
|
||
|
SC_HANDLE scm,service_handle;
|
||
|
SERVICE_DESCRIPTION service_description;
|
||
|
char filename[1024];
|
||
|
string exepath;
|
||
|
|
||
|
if(NULL==(scm=OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE)))
|
||
|
{
|
||
|
winerror(_("Error opening connection to the Service Manager."));
|
||
|
exit(-1);
|
||
|
}
|
||
|
if(0==GetModuleFileName(NULL,filename,sizeof(filename)))
|
||
|
{
|
||
|
winerror(_("Error getting the file name of the process."));
|
||
|
exit(-1);
|
||
|
}
|
||
|
|
||
|
exepath=string("\"")+filename+"\" -service";
|
||
|
|
||
|
service_handle=CreateService(
|
||
|
scm, //scm handle
|
||
|
SERVICE_SHORT_NAME, //service name
|
||
|
SERVICE_NAME, //display name
|
||
|
SERVICE_ALL_ACCESS, //desired access
|
||
|
SERVICE_WIN32_OWN_PROCESS, //service type
|
||
|
SERVICE_AUTO_START, //start type
|
||
|
SERVICE_ERROR_NORMAL, //error control
|
||
|
exepath.c_str(), //executable path with arguments
|
||
|
NULL, //load group
|
||
|
NULL, //tag for group id
|
||
|
NULL, //dependencies
|
||
|
NULL, //user name
|
||
|
NULL); //password
|
||
|
|
||
|
if(NULL==service_handle)
|
||
|
{
|
||
|
winerror("Error creating service. Already installed?");
|
||
|
exit(-1);
|
||
|
}
|
||
|
else
|
||
|
winmessage("Service successfully installed.");
|
||
|
|
||
|
//createservice doesn't have a field for description
|
||
|
//so we use ChangeServiceConfig2
|
||
|
service_description.lpDescription=SERVICE_DESCRIPTION_TEXT;
|
||
|
ChangeServiceConfig2(service_handle,SERVICE_CONFIG_DESCRIPTION,(void *)&service_description);
|
||
|
|
||
|
CloseServiceHandle(service_handle);
|
||
|
CloseServiceHandle(scm);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int service_uninstall()
|
||
|
{
|
||
|
SC_HANDLE scm,service_handle;
|
||
|
SERVICE_STATUS status;
|
||
|
|
||
|
if(NULL==(scm=OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE)))
|
||
|
{
|
||
|
winerror(_("Error opening connection to the Service Manager."));
|
||
|
exit(-1);
|
||
|
}
|
||
|
|
||
|
if(NULL==(service_handle=OpenService(scm,SERVICE_SHORT_NAME,SERVICE_QUERY_STATUS|DELETE)))
|
||
|
{
|
||
|
winerror(_("Error opening service."));
|
||
|
CloseServiceHandle(scm);
|
||
|
exit(-1);
|
||
|
}
|
||
|
|
||
|
if(0==QueryServiceStatus(service_handle,&status))
|
||
|
{
|
||
|
winerror(_("Error querying service."));
|
||
|
CloseServiceHandle(scm);
|
||
|
CloseServiceHandle(service_handle);
|
||
|
exit(-1);
|
||
|
}
|
||
|
|
||
|
if(SERVICE_STOPPED!=status.dwCurrentState)
|
||
|
{
|
||
|
winerror(SERVICE_NAME _(" is still running. Stop it before trying to uninstall it."));
|
||
|
CloseServiceHandle(scm);
|
||
|
CloseServiceHandle(service_handle);
|
||
|
exit(-1);
|
||
|
}
|
||
|
|
||
|
if(0==DeleteService(service_handle))
|
||
|
{
|
||
|
winerror(_("Error deleting service."));
|
||
|
CloseServiceHandle(scm);
|
||
|
CloseServiceHandle(service_handle);
|
||
|
exit(-1);
|
||
|
}
|
||
|
|
||
|
CloseServiceHandle(scm);
|
||
|
CloseServiceHandle(service_handle);
|
||
|
winmessage(_("Service successfully uninstalled."));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void WINAPI service_main(DWORD argc,LPTSTR *argv)
|
||
|
{
|
||
|
char *tmpstr;
|
||
|
|
||
|
//configure service_status structure
|
||
|
service_status.dwServiceType=SERVICE_WIN32;
|
||
|
service_status.dwControlsAccepted=0;
|
||
|
service_status.dwWin32ExitCode=NO_ERROR;
|
||
|
service_status.dwServiceSpecificExitCode=NO_ERROR;
|
||
|
service_status.dwCheckPoint=0;
|
||
|
service_status.dwWaitHint=0;
|
||
|
service_status.dwControlsAccepted|=SERVICE_ACCEPT_STOP;
|
||
|
|
||
|
service_status_handle=RegisterServiceCtrlHandler(SERVICE_SHORT_NAME,handler);
|
||
|
|
||
|
if(0!=service_status_handle)
|
||
|
{
|
||
|
//set service status
|
||
|
ChangeServiceStatus(service_status_handle,service_status,SERVICE_RUNNING);
|
||
|
|
||
|
//get the path to the config file
|
||
|
init_params();
|
||
|
GetModuleFileName(NULL,params[1],1024);
|
||
|
if(NULL==(tmpstr=strrchr(params[1],'\\'))) { winerror("Error finding default config file."); exit(-1); }
|
||
|
*(++tmpstr)='\0';
|
||
|
strncat(params[1],"hermes.ini",strlen("hermes.ini"));
|
||
|
|
||
|
//now start our main program
|
||
|
hermes_main(2,(char **)params);
|
||
|
|
||
|
free_params();
|
||
|
|
||
|
//when we are here, we have been stopped
|
||
|
ChangeServiceStatus(service_status_handle,service_status,SERVICE_STOP_PENDING);
|
||
|
ChangeServiceStatus(service_status_handle,service_status,SERVICE_STOPPED);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void WINAPI handler(DWORD code)
|
||
|
{
|
||
|
if(SERVICE_CONTROL_STOP==code)
|
||
|
{
|
||
|
quit=true;
|
||
|
ChangeServiceStatus(service_status_handle,service_status,SERVICE_STOP_PENDING);
|
||
|
}
|
||
|
}
|