home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
World of Shareware - Software Farm 2
/
wosw_2.zip
/
wosw_2
/
CPROG
/
KILLCO.ZIP
/
KILLCONN.C
next >
Wrap
C/C++ Source or Header
|
1991-04-09
|
22KB
|
704 lines
/****************************** KILLCONN.C ******************************
**
** KILLCONN is a program which selectively clears workstation connections
** to fileservers running under Netware versions 2.x and up. It has
** one mandatory command line parameter (an indication of who it is you want
** to kill) and a number of optional parameters. The possible parameters
** are:
**
** -help, -h, or /h: produces a help message.
** user=SOMEBODY : kill connection for user SOMEBODY.
** This parameter may contain Novell-type wildcards,
** but I recommend using the group parameter instead,
** since it keeps things cleaner administratively.
** group=SOMEGROUP : kill connections for all members of SOMEGROUP
** server=MYSERVE : by default, KILLCONN will go after the named victims
** on all the file servers it can find. Specifying a
** server will only break the victim's connection on
** the named server. Useful on internetworks when you
** only want to kill one.
** confirm=YES|NO : if confirm=YES is specified, KILLCONN will pause
** and prompt you before disconnecting each user.
** Default is NO; KILLCONN is intended for unattended
** operation.
** log=filename : writes a logfile of KILLCONN actions. A full DOS path
** can be specified. If a file with the name 'filename'
** exists, it will be overwritten.
** logadd=filename : same as log=, except that if the logfile exists,
** KILLCONN will append to it.
**
** Two new features added 4-9-91: RETRIES and WARNING
**
** warning=nn : gives user nn seconds warning before disconnecting
** retries=nn : retries the disconnect nn times before giving up.
** intended for slower networks.
**
** ...and another thing: you may optionally create a group called
** IMMORTAL on any or all file servers; KILLCONN will ignore them.
** KILLCON also ignores any attempt to disconnect any station logged in
** with the same login_name as the station running KILLCONN.
**
*/
/**************************************************************************
Some technoid stuff (I imagine if you're reading this you're at least
a little bit interested):
KILLCONN was built using BORLAND C/C++ 2.0, the TXCL interface library,
and of course the Netware API for C. The TCXL library is cheap,
excellent, and highly recommended. If for some reason you're rebuilding
this code without it (tsk-tsk: better contact the author first....)
it's only used in the do_titles, do_the_help_thing, and in
process_list to let the user know what the hell is happening. You
could easily substitute windowing routines of your own design.
The overall program strategy works like this: we first set up a loop
which will check each of the eight possible server connections at the
workstation running KILLCONN. If it finds a server, good: we proceed to
determine the maximum number of workstations which might be connected
to that server, and then check each one to see if the user connected to
it is a worthy victim as determined by the command-line parameters. We
always reject ourselves and anyone belonging to the group IMMORTAL. This
latter protects us from situations where a wildcard might be a little
too wild. If we have a worthy victim, the appropriate information is
added to a singly-linked list. After victim scanning, we walk through
the list laying waste as we go; as Arnold Schwarzenegger so aptly put
it in _Conan the Barbarian_, "We drive our enemies before us and hear
the lamentations of their women." If confirmation and logging options
are set, the program behaves accordingly.
While this probably seems like a lot of iterations, we have to account
for situations where we might have one user logged in with multiple
connections (often the case where someone is using generic login_names
for a student lab or other public facility, or where there might be
"holes" in the controlling workstation's server table or the server's
connection table caused by a connection being established and dropped
sometime prior to the life of KILLCONN.
Needless to say, the program IS copyrighted and the author, Thomas R.
Bruce, cannot be held liable for any damage you do with it. Any changes
to program code or function must be approved by the author:
Thomas R. Bruce
147 Chestnut Street E-22
Ithaca, NY 14850 607-255-1221 or 273-2661
In cyberspace:
lemuel!tom @ cs.cornell.edu
or (infrequently checked) Compuserve 75360,542
***************************************************************************/
/* assorted includes */
#ifndef _STDIO_H
#include <stdio.h>
#endif
#ifndef _STRING_H
#include <string.h>
#endif
#ifndef _CONIO_H
#include <conio.h>
#endif
#ifndef _DOS_H
#include <dos.h>
#endif
#ifndef _STDLIB_H
#include <stdlib.h>
#endif
/* TCXL headers */
#include <TCXLdef.h>
#include <TCXLwin.h>
#include <TCXLinp.h>
/* Novell API headers */
#include <nit.h>
#include <nxt.h>
#include <niterror.h>
/* assorted defines */
/* Novell API data types from their headers. Included here in the
** interest of clarity.
*/
#ifndef BYTE
#define BYTE unsigned char
#endif
#ifndef WORD
#define WORD unsigned short
#endif
#ifndef LONG
#define LONG unsigned long
#endif
/* useful stuff local to this program */
#ifndef YES
#define YES 1
#endif
#ifndef NO
#define NO 0
#endif
#define SINGLE_USER 1
#define WILD_USER 2
#define USER_GROUP 3
#define NEW_LOG 4
#define APPEND_LOG 5
#define NO_LOG 6
#define ALL_SERVERS 7
#define ONE_SERVER 8
#define TITLES 10
#define NOTITLES 11
#define CONFIRM 12
#define NOCONFIRM 13
#define WARNINGS 14
#define NOWARNINGS 15
#define ASTERISK 42 /* Yep, you got it: ASCII codes */
#define QUESMARK 63
#define SPACE 32
#define HVYCHECK 178
/* typedefs and whatnot */
typedef struct userinfo{ /* Struct to hold info about a victim */
char *username;
long objectID;
WORD connectionNumber;
struct userinfo *next;
}_userinfo;
typedef _userinfo *USERPTR;
/* function prototypes */
void parse_cmd_line(int _argc, char *_argv[]);
/* parses the command line (no kidding) */
void do_titles(void); /* takes care of screen setup, etc. */
void do_the_help_thing(int mode); /* guess what? */
USERPTR getnode(void); /* memory allocation for list node */
void freenode(USERPTR the_ptr); /* free a list node */
void add_to_list(USERPTR targ_user); /* adds user info to the victim list */
void listzap(USERPTR list_start); /* destroys list */
void process_list(void); /* processes the victim list */
int is_user_in_group(USERPTR targ_user, char *groupname);
/* well, is she? */
int is_wild_match(USERPTR targ_user, char *wildstring);
/* does username match wildcard? */
/* global variables */
USERPTR list_head;
/* program control. initialization here sets defaults */
int scope= ALL_SERVERS;
char targ_server[48]; /* target server if doing just one */
int search_type= SINGLE_USER;
char search_string[48]; /* who (all) we're looking for */
int log_type= NO_LOG;
int do_confirm= NOCONFIRM;
char log_file[256]; /* 256 is the max under Netware */
int warnings=NOWARNINGS;
int warn_time=0;
int retry_count=5;
char curr_server[48]; /* current server we're looking at */
WndT mainWin; /* handle for the main window */
/* WndT is a TCXL data type. */
void main(int argc, char *argv[]){
int i,j,k; /* typical dumb index variable(s) */
WORD serverConnID; /* connection ID of current server */
FILE_SERV_INFO *serverInfo; /* all we could want to know and more */
USERPTR this_user; /* struct for current user */
WORD objType; /* junk variable used in NWare calls */
BYTE loginTime[7];
WORD testConn; /* more junk */
char retname[48];
/* a couple of initializations */
this_user=getnode();
/* get the preliminaries out of the way */
do_titles();
parse_cmd_line(argc,argv);
/* test for IPX installation */
if (IPXInitialize() == IPX_NOT_INSTALLED) {
fprintf(stderr,"\n\tKILLCONN finds no IPX interface loaded. Aborting.\n");
exit(2);
}
/* check to see if the workstation is actually logged in to something */
serverConnID = GetPrimaryConnectionID();
if (serverConnID == 0) {
fprintf(stderr,"\n\tKILLCONN finds no primary file server. Aborting.\n");
exit(3);
}
/* Per-server loop. */
for(i=1;i<=8;i++){ /* Novell table numbering starts at 1 */
/* figure out where the hell we are */
GetFileServerName((WORD)i,curr_server);
if ((curr_server==NULL) || (strcmp(curr_server,"")==0)) continue;
if ((scope==ONE_SERVER)&& (strcmp(targ_server,curr_server)!=0)) continue;
SetPreferredConnectionID((BYTE)i);
SetPrimaryConnectionID((BYTE)i);
if(CheckConsolePrivileges()!=0){
fprintf(stderr,"\n\nNo console privileges on server %s. Aborting.", curr_server);
exit(1);
}
GetServerInformation(128,serverInfo);
/* Build the victim list. This strategy was selected over others because
it would seem that the usual environment for the program will be one
where not a lot of users are logged in. However, there is no
guarantee that the logged-in connections will be sequential, or that
one user is not logged in more than once on the file server, which
makes all the iteration necessary. */
list_head=NULL; /* zero out the list */
for (j=1;j <= serverInfo->maxConnectionsSupported; j++){
testConn=j; /* stupid way to force a type conversion,but mine own */
GetConnectionInformation(testConn,retname,
&objType,&(this_user->objectID),loginTime);
this_user->connectionNumber=j;
/* find out if the user is a worthy victim or not */
/* if there's nobody using the connection, skip onward */
if (this_user->objectID==0) continue;
/* if the user is in the IMMORTAL group, skip onward */
strcpy(this_user->username,retname);
if (is_user_in_group(this_user,"IMMORTAL")==YES) continue;
switch (search_type){
case SINGLE_USER:
if(strcmp(this_user->username,search_string)!=0) continue;
add_to_list(this_user);
break;
case WILD_USER:
if(is_wild_match(this_user, search_string)==YES) add_to_list(this_user);
break;
case USER_GROUP:
if(is_user_in_group(this_user, search_string)==YES) add_to_list(this_user);
break;
} /* end switch on search type */
} /* end of the per-connection loop */
if (list_head != NULL) process_list(); /* g'wan and kill 'em */
listzap(list_head); /* liberate the list */
} /* end of per-server loop */
clrscr();
exit(0);
} /* end of main () */
void process_list(void){
USERPTR q; /* all purpose victim pointer */
int maxstr;
FILE *out_file; /* handle for log file, if any */
char *conf_string; /* junk string for messages */
WORD myConnection; /* connection number of workstation
running this mess */
WORD msgConnList[100];
BYTE *resultList;
char *msg;
int j, k;
int confirmed;
char myname[48];
WORD myobjType;
long myobjID;
BYTE mylogTime[7]; /* all this just to find out who I am */
BYTE dateAndTime[7];
char *timeStr;
timeStr=(char *)calloc(24,sizeof(char));
myConnection=GetConnectionNumber();
GetConnectionInformation(myConnection,myname,(WORD *) &myobjType,(long *)&myobjID,(BYTE *)&mylogTime);
GetFileServerDateAndTime(dateAndTime);
sprintf(timeStr,"%02d/%02d/%02d %02d:%02d:%02d:%02d",dateAndTime[1],dateAndTime[2],dateAndTime[0],dateAndTime[3],dateAndTime[4],dateAndTime[5],dateAndTime[6]);
conf_string=(char *)calloc(128,sizeof(char));
msg=(char *)calloc(56,sizeof(char));
switch (log_type){
case NEW_LOG:
if((out_file=fopen(log_file,"wt"))==NULL)
log_type=NO_LOG;
break;
case APPEND_LOG:
if((out_file=fopen(log_file,"at"))==NULL)
log_type=NO_LOG;
break;
}
if (warnings==WARNINGS){ /* warn the little buggers */
sprintf(conf_string," User warnings being sent. Please wait.", q->username);
maxstr=max(strlen(conf_string),strlen(curr_server));
WpopUp(CNT_CNT, 0, 0, 4, maxstr+5, BOX_EXP, WHITE |_RED, WHITE|_RED);
Wshadow(_BLACK | DGREY);
Wtitle(" Warning users....",TTL_CNT,LIGHTCYAN|_RED|INTENSE|BLINK);
WprtCen(0,WHITE|_RED, curr_server);
WprtCen(1,WHITE|_RED, conf_string);
for (q=list_head; q!= NULL; q=q->next){
msgConnList[0]=q->connectionNumber;
sprintf(msg,"You will be disconnected in %d seconds. Log out.", warn_time);
SendBroadcastMessage(msg,msgConnList,resultList,(WORD)1);
}
delay(1000*warn_time); /* hang on to your hat */
Wclose();
}
for(q=list_head;q != NULL; q=q->next){
if (strcmp(q->username,myname)==0)
continue; /* wouldn't want to disconnect myself- or would I? */
if (do_confirm==CONFIRM){ /* pop up a confirmation window */
sprintf(conf_string," OK to disconnect %s (y/N)?", q->username);
maxstr=max(strlen(conf_string),strlen(curr_server));
WpopUp(CNT_CNT, 0, 0, 4, maxstr+5, BOX_EXP, WHITE |_RED, WHITE|_RED);
Wshadow(_BLACK | DGREY);
Wtitle(" Are you sure? ",TTL_CNT,LIGHTCYAN|_RED|INTENSE|BLINK);
WprtCen(0,WHITE|_RED, curr_server);
WprtCen(1,WHITE|_RED, conf_string);
/* manual-abort handler */
if(KwGetYn(NO)!='Y'){
Wclose();
sprintf(conf_string,"Disconnect of %s aborted.",q->username);
maxstr=max(strlen(conf_string),strlen(curr_server));
WpopUp(CNT_CNT, 0, 0, 4, maxstr+5, BOX_EXP, WHITE |_RED, WHITE|_RED);
Wshadow(_BLACK | DGREY);
Wtitle(" Cancelling... ",TTL_CNT,LIGHTCYAN|_RED|INTENSE|BLINK);
WprtCen(0,WHITE|_RED, curr_server);
WprtCen(1,WHITE|_RED, conf_string);
if(log_type != NO_LOG) fprintf(out_file,"%s\tDisconnect of %s from %s was aborted by operator.\n",timeStr,q->username,curr_server);
delay(2000);
continue;
}
Wclose();
}
/* leave some evidence at the workstation */
msgConnList[0]=q->connectionNumber;
sprintf(msg,"Station cleared by KILLCONN at %s",timeStr);
SendBroadcastMessage(msg,msgConnList,resultList,(WORD)1);
confirmed=1;
for(j=0;j < retry_count && confirmed!=0; ++j){
sprintf(conf_string,"Disconnecting %s.", q->username);
maxstr=max(strlen(conf_string),strlen(curr_server));
WpopUp(CNT_CNT, 0, 0, 4, maxstr+5, BOX_EXP, WHITE |_RED, WHITE|_RED);
Wshadow(_BLACK | DGREY);
Wtitle(" Clearing connection... ",TTL_CNT,LIGHTCYAN | _RED|INTENSE|BLINK);
WprtCen(0,WHITE|_RED, curr_server);
WprtCen(1,WHITE|_RED, conf_string);
delay(3000); /* to permit message to arrive */
confirmed=ClearConnectionNumber(q->connectionNumber); /* He's dead, Jim */
}
if((log_type != NO_LOG) && (confirmed==0)) fprintf(out_file,"%s\t %s was disconnected from %s.\n",timeStr,q->username,curr_server);
if((log_type != NO_LOG) && (confirmed!=0)) fprintf(out_file,"%s\t %s disconnect from %s failed.\n",timeStr,q->username,curr_server);
if (confirmed!=0){
Wclose();
sprintf(conf_string,"Attempted disconnect of %s failed.", q->username);
maxstr=max(strlen(conf_string),strlen(curr_server));
WpopUp(CNT_CNT, 0, 0, 4, maxstr+5, BOX_EXP, WHITE |_RED, WHITE|_RED);
Wshadow(_BLACK | DGREY);
Wtitle(" Disconnect failed ",TTL_CNT,LIGHTCYAN | _RED|INTENSE|BLINK);
WprtCen(0,WHITE|_RED, curr_server);
WprtCen(1,WHITE|_RED, conf_string);
}
}
/* clean up for this server-pass */
free (conf_string);
free (timeStr);
fcloseall();
if(Wisactiv(mainWin)==FALSE) Wclose();
}
int is_user_in_group(USERPTR targ_user, char *groupname){
int retcode;
retcode = IsBinderyObjectInSet(groupname, OT_USER_GROUP, "GROUP_MEMBERS",targ_user->username, OT_USER);
if (retcode == 0) return (YES);
else return (NO);
}
int is_wild_match(USERPTR targ_user, char *wildstring){
int cCode=0;
WORD srchObjType; /* type of object we're scanning for;
the name can be wildcarded,
which is why we're using this
API call */
long objID= -1L;
char retName[48]; /* name returned by scanning function */
WORD objType; /* better damn well be OT_USER and not
a group or something else */
char objHasProperties;
char objFlag;
char objSecurity;
while (cCode==0){
cCode=ScanBinderyObject(wildstring, OT_USER, &objID,
retName, &objType, &objHasProperties, &objFlag, &objSecurity);
if ((strcmp(retName,targ_user->username)==0)&& (objType==OT_USER))
return(YES);
}
return(NO);
}
void do_titles(void){
TcxlInit();
WsetFil(HVYCHECK);
mainWin=Wopen(0,0,24,79,BOX_EXP,WHITE|_BLUE,CYAN|_BLUE);
WsetFil(SPACE);
Wopen(0,0,7,28,BOX_EXP,WHITE|_BLUE,WHITE|_BLUE);
Wshadow(_BLACK | DGREY);
WprtCen(1,WHITE|_BLUE,"KILLCONN");
WprtCen(2,WHITE|_BLUE,"A Connection-Killer");
WprtCen(3,WHITE|_BLUE,"for Novell Netware");
WprtCen(5,WHITE|_BLUE,"(C) 1991 Thomas R. Bruce");
Wslide(17,49);
Wslide(2,49);
Wslide(17,2);
Wcenter(CNT_CNT);
delay(3000);
Wclose();
Wopen(1,1,1,78,BOX_SPA,WHITE|_BLUE,WHITE|_BLUE);
WprtCen(0,WHITE|_BLUE,"KILLCONN copyright (C)1991 Thomas R. Bruce");
Wactiv(mainWin);
return;
}
void do_the_help_thing(int mode){
if (mode==TITLES)do_titles();
WpopUp(CNT_CNT,0,0,16,60,BOX_EXP,WHITE|_RED,WHITE|_RED);
Wshadow(_BLACK | DGREY);
Wtitle(" Help! ", TTL_CNT,WHITE|_RED);
Wprts(0,4,LCYAN|_RED,"Command line syntax:");
Wprts(2,4,WHITE|_RED,"killconn who-options [other-options]");
Wprts(4,4,LCYAN|_RED,"where who-options is one of:");
Wprts(6,8,WHITE|_RED,"user=SOMEBODY (can contain wildcard chars)");
Wprts(7,8,WHITE|_RED,"group=USERGROUP");
Wprts(9,4,LCYAN|_RED,"and [other-options] are:");
Wprts(11,8,WHITE|_RED,"server=MYSERVE (for a single server)");
Wprts(12,8,WHITE|_RED,"confirm=YES to check before disconnecting");
Wprts(13,8,WHITE|_RED,"log=filename to log to a new text file");
Wprts(14,8,WHITE|_RED,"logadd=filename to append to an existing log");
KwGetCh();
exit(0);
}
void parse_cmd_line(int _argc, char *_argv[])
{
int j;
char *arg_lead; /* server= or whatever */
char *arg_targ; /* actual 'content' of argument specification */
char *arg_temp;
/* No args. Indicates confused user. Give 'em help, quick */
/* Leaving this structured this way provides a socket for a
possible menu-driven version someday */
if (_argc==1) do_the_help_thing(NOTITLES);
for (j = 1; j < _argc; ++j) {
arg_temp = (char *) strupr(_argv[j]);
if ((strcmp(arg_temp, "-H") == 0)||(strcmp(arg_temp, "/H") == 0)||
(strcmp(arg_temp, "HELP") == 0)) do_the_help_thing(NOTITLES);
/* now the slightly more complex ones */
arg_lead = (char *) strupr(strtok(_argv[j], "="));
if (!arg_lead){
fprintf(stderr,"\nBad command line argument. Terminating.");
exit(4);
}
arg_targ = (char *) strupr(strtok(NULL, "="));
if (strcmp(arg_lead, "USER") == 0) {
if ((strchr(arg_targ,ASTERISK)!=NULL) ||
(strchr(arg_targ,QUESMARK)!=NULL)) search_type=WILD_USER;
else search_type= SINGLE_USER;
strcpy(search_string, arg_targ);
continue;
}
if (strcmp(arg_lead, "GROUP") == 0) {
search_type=USER_GROUP;
strcpy(search_string, arg_targ);
continue;
}
if (strcmp(arg_lead, "SERVER") == 0) {
scope=ONE_SERVER;
strcpy(targ_server,arg_targ);
continue;
}
if (strcmp(arg_lead, "CONFIRM") == 0) {
if (strcmp(arg_targ,"YES")==0) do_confirm=CONFIRM;
continue;
}
if (strcmp(arg_lead, "LOG") == 0) {
log_type=NEW_LOG;
strcpy(log_file, arg_targ);
continue;
}
if (strcmp(arg_lead, "LOGADD") == 0) {
log_type=APPEND_LOG;
strcpy(log_file, arg_targ);
continue;
}
/* Code added 4-9-91 to accomodate two new features: retries and
warnings */
if (strcmp(arg_lead, "WARNING") == 0) {
warnings=WARNINGS;
warn_time=atoi(arg_targ);
continue;
}
if (strcmp(arg_lead, "RETRIES") == 0) {
retry_count=atoi(arg_targ);
continue;
}
printf("Incomprehensible command line argument type ");
printf(arg_lead);
printf(" entered. Shame on you. Any key terminates.");
getch();
exit(1);
}
}
/* Function to add nodes to the victim list */
void add_to_list(USERPTR targ_user){
USERPTR p,q;
p=getnode();
strcpy(p->username,targ_user->username);
p->objectID=targ_user->objectID;
p->connectionNumber=targ_user->connectionNumber;
q=list_head;
list_head=p;
p->next=q;
}
/* Function which allocates memory for the victim list nodes */
USERPTR getnode(void){
USERPTR p;
p=(USERPTR)malloc(sizeof(struct userinfo));
p->username=(char *)calloc(48,sizeof(char));
return (p);
}
/* Function to free up nodes */
void freenode(USERPTR the_ptr){
free(the_ptr->username);
free(the_ptr);
}
/* Function to zap a whole list */
void listzap(USERPTR list_start){
USERPTR p,q;
for (p=list_start;p==NULL;p=q){
q=p->next;
freenode(p);
}
return;
}
/****************************************************************************
That's all, folks.
Completed 04/06/91 by TRB.
*****************************************************************************/