public inbox for cluster-cvs@sourceware.org
help / color / mirror / Atom feed
* cluster: STABLE3 - fence_agents: Replaced telnet_ssl by fence_nss_wrapper
@ 2009-04-01 9:48 Jan Friesse
0 siblings, 0 replies; only message in thread
From: Jan Friesse @ 2009-04-01 9:48 UTC (permalink / raw)
To: cluster-cvs-relay
Gitweb: http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=a68e594ae0461a82b8364b2a88e0698c0b0cad46
Commit: a68e594ae0461a82b8364b2a88e0698c0b0cad46
Parent: 451c53d7b8f92e6e35bf871606a5b7286852b9a7
Author: Jan Friesse <jfriesse@redhat.com>
AuthorDate: Wed Mar 25 17:00:43 2009 +0100
Committer: Jan Friesse <jfriesse@redhat.com>
CommitterDate: Wed Apr 1 11:47:50 2009 +0200
fence_agents: Replaced telnet_ssl by fence_nss_wrapper
Fence_nss_wrapper is drop-on replacement of telnet_ssl.
This is hobbit like tool with support for NSS (SSL)
connection.
In contrast of old tool, this has many advantages:
- Support for IPv6 (user can force IPv4)
- Use polling instead of active waiting (non-blocking
socket + sleep)
- It's based on NSS (future of Fedora SSL)
- Support for service names (not used in agents)
---
fence/agents/lib/Makefile | 2 +-
fence/agents/lib/fencing.py.py | 2 +-
fence/agents/lib/telnet_ssl.py | 72 ----
fence/agents/nss_wrapper/Makefile | 24 ++
fence/agents/nss_wrapper/fence_nss_wrapper.c | 483 ++++++++++++++++++++++++++
make/fencebuild.mk | 1 +
6 files changed, 510 insertions(+), 74 deletions(-)
diff --git a/fence/agents/lib/Makefile b/fence/agents/lib/Makefile
index 049ff6e..2cccd98 100644
--- a/fence/agents/lib/Makefile
+++ b/fence/agents/lib/Makefile
@@ -1,6 +1,6 @@
include ../../../make/defines.mk
-TARGET= fencing.py telnet_ssl fencing_snmp.py
+TARGET= fencing.py fencing_snmp.py
FENCEAGENTSLIB= $(TARGET)
diff --git a/fence/agents/lib/fencing.py.py b/fence/agents/lib/fencing.py.py
index e5473e1..34b0caa 100644
--- a/fence/agents/lib/fencing.py.py
+++ b/fence/agents/lib/fencing.py.py
@@ -30,7 +30,7 @@ EC_STATUS_HMC = 9
TELNET_PATH = "/usr/bin/telnet"
SSH_PATH = "/usr/bin/ssh"
-SSL_PATH = "@FENCEAGENTSLIBDIR@/telnet_ssl"
+SSL_PATH = "@SBINDIR@/fence_nss_wrapper"
all_opt = {
"help" : {
diff --git a/fence/agents/lib/telnet_ssl.py b/fence/agents/lib/telnet_ssl.py
deleted file mode 100644
index 5d0c981..0000000
--- a/fence/agents/lib/telnet_ssl.py
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/bin/python
-
-#####
-## simple telnet client with SSL support
-##
-## ./telnet_ssl host port
-#####
-
-import sys, socket, string, fcntl, os , time
-from OpenSSL import SSL
-
-#BEGIN_VERSION_GENERATION
-RELEASE_VERSION=""
-REDHAT_COPYRIGHT=""
-BUILD_DATE=""
-#END_VERSION_GENERATION
-
-def main():
- hostname = None
- port = None
-
- if (len(sys.argv) != 3):
- print "Error: You have to enter hostname and port number\n"
- sys.exit(-1)
-
- hostname = sys.argv[1]
- port = int(sys.argv[2])
-
- try:
- s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
- s.connect((hostname,port))
- ctx = SSL.Context(SSL.SSLv23_METHOD)
- conn = SSL.Connection(ctx, s)
- conn.set_connect_state()
- except socket.error, e:
- print "Error: Unable to connect to %s:%s %s" % (hostname, port, str(e))
- sys.exit(-1)
-
- fcntl.fcntl(sys.stdin, fcntl.F_SETFL, os.O_NONBLOCK)
- s.settimeout(0)
-
- while 1:
- try:
- write_buff = sys.stdin.readline()
- if (len(write_buff) > 0):
- write_buff = string.rstrip(write_buff)
- i = 10
- while i > 0:
- i = i-1
- try:
- conn.send(write_buff + "\r\n")
- i = -1
- except SSL.WantReadError:
- ## We have to wait for connect, mostly just for first time
- time.sleep(1)
- if i == 0:
- sys.exit(-2)
- except IOError:
- 1
-
- try:
- read_buff = conn.recv(4096)
- print read_buff
- sys.stdout.flush()
- except SSL.WantReadError:
- 1
- except SSL.ZeroReturnError:
- break
-
-
-if __name__ == "__main__":
- main()
diff --git a/fence/agents/nss_wrapper/Makefile b/fence/agents/nss_wrapper/Makefile
new file mode 100644
index 0000000..ae74124
--- /dev/null
+++ b/fence/agents/nss_wrapper/Makefile
@@ -0,0 +1,24 @@
+TARGET= fence_nss_wrapper
+
+SBINDIRT=$(TARGET)
+
+all: ${TARGET}
+
+include ../../../make/defines.mk
+include $(OBJDIR)/make/cobj.mk
+include $(OBJDIR)/make/clean.mk
+include $(OBJDIR)/make/install.mk
+include $(OBJDIR)/make/uninstall.mk
+
+OBJS= fence_nss_wrapper.o
+
+CFLAGS += -I${incdir} -I${nsprincdir} -I${nssincdir}
+
+LDFLAGS += -L${libdir} -L${nsprlibdir} -L${nsslibdir} -lnss3 -lssl3
+
+${TARGET}: ${OBJS}
+ $(CC) -o $@ $^ $(LDFLAGS)
+
+clean: generalclean
+
+-include $(OBJS:.o=.d)
diff --git a/fence/agents/nss_wrapper/fence_nss_wrapper.c b/fence/agents/nss_wrapper/fence_nss_wrapper.c
new file mode 100644
index 0000000..c6b3cda
--- /dev/null
+++ b/fence/agents/nss_wrapper/fence_nss_wrapper.c
@@ -0,0 +1,483 @@
+/** @file fence_nss_wrapper.c - Main source code of hobbit like tool with
+ support for NSS (SSL) connection.
+*/
+#include <stdio.h>
+#include <nss.h>
+#include <ssl.h>
+#include <prio.h>
+#include <prnetdb.h>
+#include <prerror.h>
+#include <prinit.h>
+#include <getopt.h>
+#include <libgen.h>
+
+/*---- CONSTANTS -------------*/
+
+/** Default operation = connect and telnet*/
+#define OPERATION_DEFAULT 0
+/** Operation display help*/
+#define OPERATION_HELP 1
+
+/** Default mode of connection. Try first found working address*/
+#define MODE_DEFAULT 3
+/** Use only IPv4*/
+#define MODE_IP4MODE 1
+/** Use only IPv6*/
+#define MODE_IP6MODE 2
+/** Use RAW mode - no change of \r and \n to \r\n*/
+#define MODE_RAW 4
+/** Use non-secure mode (without SSL, only pure socket)*/
+#define MODE_NO_SSL 8
+
+/*------ Functions ---------------*/
+
+/** Return port inserted in string. Fuction tests, if port is integer, and than return
+ integer value of string. Otherwise, it will use /etc/services. On fail, it returns
+ port -1.
+ @param port_s Input port or service name
+ @return port number (converted with ntohs) on success, otherwise -1.
+*/
+int return_port(char *port_s) {
+ char *end_c;
+ int res;
+ struct servent *serv;
+
+ res=strtol(port_s,&end_c,10);
+
+ if (*end_c=='\0') return res;
+
+ /*It's not number, so try service name*/
+ serv=getservbyname(port_s,NULL);
+
+ if (serv==NULL) return -1;
+
+ return ntohs(serv->s_port);
+}
+
+/** Hook handler for bad certificate (because we have no DB, EVERY certificate is bad).
+ Returned value is always SECSuccess = it's ok certificate.
+ @param arg NULL value
+ @param fd socket cased error
+ @return SECSuccess.
+*/
+SECStatus nss_bad_cert_hook(void *arg,PRFileDesc *fd) {
+ return SECSuccess;
+}
+
+/** Display last NSPR/NSS error code and user readable message.
+*/
+void print_nspr_error(void) {
+ fprintf(stderr,"Error (%d): %s\n",PR_GetError(),PR_ErrorToString(PR_GetError(),PR_LANGUAGE_I_DEFAULT));
+}
+
+/** Initialize NSS. NSS is initialized without DB and with
+ domnestic policy.
+ @return 1 on success, otherwise 0.
+*/
+int init_nss(void) {
+ if ((NSS_NoDB_Init(NULL)!=SECSuccess) ||
+ (NSS_SetDomesticPolicy()!=SECSuccess)) {
+ print_nspr_error();
+
+ return 0;
+ }
+
+ SSL_ClearSessionCache();
+
+ return 1;
+}
+
+/** Create socket. If ssl is >0, socket is ssl enabled.
+ @param ssl Enable ssl (Client, SSL2+3, no TLS, compatible hello) if PR_TRUE, otherwise no.
+ @param ipv6 New socket will be IPv4 if this value is 0, otherwise it will be ipv6
+ @return NULL on error, otherwise socket.
+*/
+PRFileDesc *create_socket(int ssl,int ipv6) {
+ PRFileDesc *res_socket;
+
+ res_socket=PR_OpenTCPSocket((ipv6?PR_AF_INET6:PR_AF_INET));
+ if (res_socket==NULL) {
+ print_nspr_error();
+
+ return NULL;
+ }
+
+ if (!ssl) return res_socket;
+
+ if (!(res_socket=SSL_ImportFD(NULL,res_socket))) {
+ print_nspr_error();
+
+ return NULL;
+ }
+
+ if ((SSL_OptionSet(res_socket,SSL_SECURITY,ssl)!=SECSuccess) ||
+ (SSL_OptionSet(res_socket,SSL_HANDSHAKE_AS_SERVER,PR_FALSE)!=SECSuccess) ||
+ (SSL_OptionSet(res_socket,SSL_HANDSHAKE_AS_CLIENT,PR_TRUE)!=SECSuccess) ||
+ (SSL_OptionSet(res_socket,SSL_ENABLE_SSL2,ssl)!=SECSuccess) ||
+ (SSL_OptionSet(res_socket,SSL_ENABLE_SSL3,ssl)!=SECSuccess) ||
+ (SSL_OptionSet(res_socket,SSL_ENABLE_TLS,PR_FALSE)!=SECSuccess) ||
+ (SSL_OptionSet(res_socket,SSL_V2_COMPATIBLE_HELLO,ssl)!=SECSuccess) ||
+ (SSL_SetPKCS11PinArg(res_socket,NULL)==-1) ||
+ (SSL_AuthCertificateHook(res_socket,SSL_AuthCertificate,CERT_GetDefaultCertDB())!=SECSuccess) ||
+ (SSL_BadCertHook(res_socket,nss_bad_cert_hook,NULL)!=SECSuccess)) {
+ print_nspr_error();
+
+ if (PR_Close(res_socket)!=PR_SUCCESS) {
+ print_nspr_error();
+ }
+
+ return NULL;
+ }
+
+ return res_socket;
+}
+
+/** Create socket and connect to it.
+ @param hostname Hostname to connect
+ @param port Port name/number to connect
+ @param mode Connection mode. Bit-array of MODE_NO_SSL, MODE_IP6MODE, MODE_IP4MODE.
+ @return NULL on error, otherwise connected socket.
+*/
+PRFileDesc *create_connected_socket(char *hostname,int port,int mode) {
+ PRAddrInfo *addr_info;
+ void *addr_iter;
+ PRNetAddr addr;
+ PRFileDesc *socket;
+ int can_exit,valid_socket;
+ PRUint16 af_spec;
+
+ socket=NULL;
+
+ addr_info=NULL;
+
+ af_spec=PR_AF_UNSPEC;
+
+ if (!(mode&MODE_IP6MODE)) af_spec=PR_AF_INET;
+
+ addr_info=PR_GetAddrInfoByName(hostname,af_spec,PR_AI_ADDRCONFIG);
+
+ if (addr_info == NULL) {
+ print_nspr_error();
+ return NULL;
+ }
+
+ /*We have socket -> enumerate and try to connect*/
+ addr_iter=NULL;
+ can_exit=0;
+ valid_socket=0;
+
+ while (!can_exit) {
+ addr_iter=PR_EnumerateAddrInfo(addr_iter,addr_info,port,&addr);
+
+ if (addr_iter==NULL) {
+ can_exit=1;
+ } else {
+ if ((PR_NetAddrFamily(&addr)==PR_AF_INET && (mode&MODE_IP4MODE)) ||
+ (PR_NetAddrFamily(&addr)==PR_AF_INET6 && (mode&MODE_IP6MODE))) {
+ /*Type of address is what user want, try to create socket and make connection*/
+
+ /*Create socket*/
+ socket=create_socket(!(mode&MODE_NO_SSL),(PR_NetAddrFamily(&addr)==PR_AF_INET6));
+
+ if (socket) {
+ /*Try to connect*/
+ if (PR_Connect(socket,&addr,PR_INTERVAL_NO_TIMEOUT)==PR_SUCCESS) {
+ /*Force handshake*/
+ if ((!(mode&MODE_NO_SSL)) && SSL_ForceHandshake(socket)!=SECSuccess) {
+ /*Handhake failure -> fail*/
+ print_nspr_error();
+ if (PR_Close(socket)!=PR_SUCCESS) {
+ print_nspr_error();
+ can_exit=1;
+ }
+ socket=NULL;
+ }
+
+ /*Socket is connected -> we can return it*/
+ can_exit=1;
+ } else {
+ /*Try another address*/
+ if (PR_Close(socket)!=PR_SUCCESS) {
+ print_nspr_error();
+ can_exit=1;
+ }
+ socket=NULL;
+ }
+ }
+ }
+ }
+ }
+
+ if (!socket) {
+ /*Socket is unvalid -> we don't found any usable address*/
+ fprintf(stderr,"Can't connect to host %s on port %d!\n",hostname,port);
+ }
+
+ PR_FreeAddrInfo(addr_info);
+
+ return socket;
+}
+
+/** Parse arguments from command line.
+ @param argc Number of arguments in argv
+ @param argv Array of arguments
+ @param mode Pointer to int will be filled with OPERATION_DEFAULT or OPERATION_HELP.
+ @param mode Pointer to int will be filled with MODE_DEFAULT, MODE_IP4MODE or MODE_IP4MODE.
+ @return 1 on success, otherwise 0.
+*/
+int parse_cli(int argc,char *argv[],int *operation,int *mode,char **hostname,char **port) {
+ int opt;
+
+ *operation=OPERATION_DEFAULT;
+ *mode=MODE_DEFAULT;
+ *port=NULL;
+ *hostname=NULL;
+
+ while ((opt=getopt(argc,argv,"h46rz"))!=-1) {
+ switch (opt) {
+ case 'h':
+ *operation=OPERATION_HELP;
+
+ return 0;
+ break;
+
+ case '4':
+ (*mode)&=~MODE_IP6MODE;
+ (*mode)|=MODE_IP4MODE;
+ break;
+
+ case '6':
+ (*mode)&=~MODE_IP4MODE;
+ (*mode)|=MODE_IP6MODE;
+ break;
+
+ case 'r':
+ (*mode)|=MODE_RAW;
+ break;
+
+ case 'z':
+ (*mode)|=MODE_NO_SSL;
+ break;
+
+ default:
+ return 0;
+ break;
+ }
+ }
+
+ if (argc-optind<2) {
+ fprintf(stderr,"Hostname and port is expected!\n");
+
+ return 0;
+ }
+
+ *hostname=argv[optind];
+ *port=argv[optind+1];
+
+ return 1;
+}
+
+/** Show usage of application.
+ @param pname Name of program (usually basename of argv[0])
+*/
+void show_usage(char *pname) {
+ printf("usage: %s [options] hostname port\n", pname);
+ printf(" -4 Force to use IPv4\n");
+ printf(" -6 Force to use IPv6\n");
+ printf(" -r Use RAW connection (don't convert \\r and \\n characters)\n");
+ printf(" -z Don't use SSL connection (use pure socket)\n");
+ printf(" -h Show this help\n");
+}
+
+/** Convert End Of Lines (Unix \n, Macs \r or DOS/Win \r\n) to \r\n.
+ @param in_buffer Input buffer
+ @param in_size Input buffer size
+ @param out_buffer Output buffer (must be prealocated). Should be (2*in_size) (in worst case)
+ @param out_size There will be size of out_buffer
+ @param in_state Internal state of finite automata. First call should have this 0, other calls
+ shouldn't change this value. After end of file, you may add to this value +100 and call this
+ again, to make sure of proper end (in_buffer can be in this case everything, including NULL).
+*/
+void convert_eols(char *in_buffer,int in_size,char *out_buffer,int *out_size,int *in_state) {
+ int in_pos,out_pos;
+ int status;
+ char in_char;
+
+ out_pos=0;
+ status=*in_state;
+
+ if (status==100 || status==101) {
+ if (status==101) {
+ out_buffer[out_pos++]='\r';
+ out_buffer[out_pos++]='\n';
+ }
+ } else {
+ for (in_pos=0;in_pos<in_size;in_pos++) {
+ in_char=in_buffer[in_pos];
+
+ switch (status) {
+ case 0:
+ if (in_char=='\r') status=1;
+ if (in_char=='\n') {
+ out_buffer[out_pos++]='\r';
+ out_buffer[out_pos++]='\n';
+ }
+ if ((in_char!='\r') && (in_char!='\n')) out_buffer[out_pos++]=in_char;
+ break;
+
+ case 1:
+ out_buffer[out_pos++]='\r';
+ out_buffer[out_pos++]='\n';
+
+ if (in_char!='\n') out_buffer[out_pos++]=in_char;
+
+ status=0;
+ break;
+ }
+ }
+ }
+
+ *out_size=out_pos;
+ *in_state=status;
+}
+
+/** Start polling cycle.
+ @param socket Network connected socket.
+ @param mode Bit-array of MODE_*. This function take care on MODE_RAW.
+ @return 0 on failure, otherwise 1
+*/
+int poll_cycle(PRFileDesc *socket,int mode) {
+ PRPollDesc pool[2];
+ char buffer[1024],buffer_eol[1024*2];
+ int readed_bytes;
+ int can_exit;
+ int res;
+ int bytes_to_write;
+ int eol_state;
+
+ can_exit=0;
+ eol_state=0;
+
+ /* Fill pool*/
+ pool[1].fd=socket;
+ pool[0].fd=PR_STDIN;
+ pool[0].in_flags=pool[1].in_flags=PR_POLL_READ;
+ pool[0].out_flags=pool[1].out_flags=0;
+
+ while (!can_exit) {
+ res=(PR_Poll(pool,sizeof(pool)/sizeof(PRPollDesc),PR_INTERVAL_NO_TIMEOUT));
+
+ if (res==-1) {
+ print_nspr_error();
+
+ return 0;
+ }
+
+ if (pool[1].out_flags==PR_POLL_READ) {
+ /*We have something in socket*/
+ if ((readed_bytes=PR_Read(pool[1].fd,buffer,sizeof(buffer)))>0) {
+ if (PR_Write(PR_STDOUT,buffer,readed_bytes)!=readed_bytes) {
+ print_nspr_error();
+
+ return 0;
+ }
+ } else {
+ /*End of stream -> quit*/
+ can_exit=1;
+ }
+ }
+
+ if (pool[0].out_flags==PR_POLL_READ) {
+ /*We have something in stdin*/
+ if ((readed_bytes=PR_Read(pool[0].fd,buffer,sizeof(buffer)))>0) {
+
+ if (!(mode&MODE_RAW)) {
+ convert_eols(buffer,readed_bytes,buffer_eol,&bytes_to_write,&eol_state);
+ } else
+ bytes_to_write=readed_bytes;
+
+ if (PR_Write(pool[1].fd,(mode&MODE_RAW?buffer:buffer_eol),bytes_to_write)!=bytes_to_write) {
+ print_nspr_error();
+
+ return 0;
+ }
+ } else {
+ /*End of stream -> quit*/
+ if (!(mode&MODE_RAW)) {
+ convert_eols(NULL,0,buffer_eol,&bytes_to_write,&eol_state);
+ if (PR_Write(pool[1].fd,buffer_eol,bytes_to_write)!=bytes_to_write) {
+ print_nspr_error();
+
+ return 0;
+ }
+ }
+
+ can_exit=1;
+ }
+ }
+
+ pool[0].out_flags=pool[1].out_flags=0;
+ } /*while (!can_exit)*/
+
+ return 1;
+}
+
+void atexit_handler(void) {
+ if (PR_Initialized())
+ PR_Cleanup();
+
+ if (fclose(stdout)!=0) {
+ fprintf(stderr,"Can't close stdout!\n");
+
+ exit(1);
+ }
+}
+
+/** Entry point of application.
+ @param argc Number of arguments on command line
+ @param argv Array of strings with arguments from command line
+ @return 0 on success, otherwise >0.
+*/
+int main(int argc,char *argv[]) {
+ int mode,operation;
+ char *hostname, *port;
+ char *pname;
+ int port_n;
+ PRFileDesc *fd_socket;
+ int res;
+
+ pname=basename(argv[0]);
+
+ atexit(atexit_handler);
+
+ if (!parse_cli(argc,argv,&operation,&mode,&hostname,&port) || operation==OPERATION_HELP) {
+ show_usage(pname);
+
+ if (operation!=OPERATION_HELP) return 1;
+
+ return 0;
+ }
+
+ if ((port_n=return_port(port))==-1) {
+ fprintf(stderr,"Error. Unknown port number/name %s!\n",port);
+
+ return 1;
+ }
+
+ if (!(mode&MODE_NO_SSL)) {
+ if (!init_nss()) return 1;
+ }
+
+ if (!(fd_socket=create_connected_socket(hostname,port_n,mode)))
+ return 1;
+
+ res=poll_cycle(fd_socket,mode);
+
+ if (PR_Close(fd_socket)!=PR_SUCCESS) {
+ print_nspr_error();
+
+ return 1;
+ }
+
+ return (res?0:1);
+}
diff --git a/make/fencebuild.mk b/make/fencebuild.mk
index 7c5f0c5..48145b7 100644
--- a/make/fencebuild.mk
+++ b/make/fencebuild.mk
@@ -19,6 +19,7 @@ $(TARGET):
-e 's#@FENCEAGENTSLIBDIR@#${fenceagentslibdir}#g' \
-e 's#@SNMPBIN@#${snmpbin}#g' \
-e 's#@LOGDIR@#${logdir}#g' \
+ -e 's#@SBINDIR@#${sbindir}#g' \
> $@
clean: generalclean
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2009-04-01 9:48 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-04-01 9:48 cluster: STABLE3 - fence_agents: Replaced telnet_ssl by fence_nss_wrapper Jan Friesse
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).