Add bin and edit workflow
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 9s

This commit is contained in:
2026-09-16 13:11:16 -06:00
parent c8ac4fcae5
commit 4cee170d66
17576 changed files with 895740 additions and 2 deletions
+269
View File
@@ -0,0 +1,269 @@
SHELL = /bin/sh
# V=0 quiet, V=1 verbose. other values don't work.
V = 0
V0 = $(V:0=)
Q1 = $(V:1=)
Q = $(Q1:0=@)
ECHO1 = $(V:1=@ :)
ECHO = $(ECHO1:0=@ echo)
NULLCMD = :
#### Start of system configuration section. ####
srcdir = .
topdir = /usr/include/ruby-3.2.0
hdrdir = $(topdir)
arch_hdrdir = /usr/include/x86_64-linux-gnu/ruby-3.2.0
PATH_SEPARATOR = :
VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
prefix = $(DESTDIR)/usr
rubysitearchprefix = $(sitearchlibdir)/$(RUBY_BASE_NAME)
rubyarchprefix = $(archlibdir)/$(RUBY_BASE_NAME)
rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
exec_prefix = $(prefix)
vendorarchhdrdir = $(sitearchincludedir)/$(RUBY_VERSION_NAME)/vendor_ruby
sitearchhdrdir = $(sitearchincludedir)/$(RUBY_VERSION_NAME)/site_ruby
rubyarchhdrdir = $(archincludedir)/$(RUBY_VERSION_NAME)
vendorhdrdir = $(rubyhdrdir)/vendor_ruby
sitehdrdir = $(rubyhdrdir)/site_ruby
rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME)
vendorarchdir = $(rubysitearchprefix)/vendor_ruby/$(ruby_version)
vendorlibdir = $(vendordir)/$(ruby_version)
vendordir = $(rubylibprefix)/vendor_ruby
sitearchdir = $(DESTDIR)/usr/local/lib/x86_64-linux-gnu/site_ruby
sitelibdir = $(sitedir)/$(ruby_version)
sitedir = $(DESTDIR)/usr/local/lib/site_ruby
rubyarchdir = $(rubyarchprefix)/$(ruby_version)
rubylibdir = $(rubylibprefix)/$(ruby_version)
sitearchincludedir = $(includedir)/$(sitearch)
archincludedir = $(includedir)/$(arch)
sitearchlibdir = $(libdir)/$(sitearch)
archlibdir = $(libdir)/$(arch)
ridir = $(datarootdir)/$(RI_BASE_NAME)
mandir = $(datarootdir)/man
localedir = $(datarootdir)/locale
libdir = $(exec_prefix)/lib
psdir = $(docdir)
pdfdir = $(docdir)
dvidir = $(docdir)
htmldir = $(docdir)
infodir = $(datarootdir)/info
docdir = $(datarootdir)/doc/$(PACKAGE)
oldincludedir = $(DESTDIR)/usr/include
includedir = $(prefix)/include
runstatedir = $(DESTDIR)/var/run
localstatedir = $(DESTDIR)/var
sharedstatedir = $(prefix)/com
sysconfdir = $(DESTDIR)/etc
datadir = $(datarootdir)
datarootdir = $(prefix)/share
libexecdir = $(exec_prefix)/libexec
sbindir = $(exec_prefix)/sbin
bindir = $(exec_prefix)/bin
archdir = $(rubyarchdir)
CC_WRAPPER =
CC = x86_64-linux-gnu-gcc
CXX = x86_64-linux-gnu-g++
LIBRUBY = $(LIBRUBY_SO)
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static $(MAINLIBS)
empty =
OUTFLAG = -o $(empty)
COUTFLAG = -o $(empty)
CSRCFLAG = $(empty)
RUBY_EXTCONF_H =
cflags = $(optflags) $(debugflags) $(warnflags)
cxxflags =
optflags = -O3 -fno-fast-math
debugflags = -ggdb3
warnflags = -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wmisleading-indentation -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wundef
cppflags =
CCDLFLAGS = -fPIC
CFLAGS = $(CCDLFLAGS) -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=BUILDDIR=. -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection -fdebug-prefix-map=BUILDDIR=/usr/src/ruby3.2-3.2.3-1ubuntu0.24.04.8 -fPIC $(ARCH_FLAG)
INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
DEFS =
CPPFLAGS = -DHAVE_OPENSSL_SSL_H -DHAVE_OPENSSL_ERR_H -DWITH_SSL -DBUILD_FOR_RUBY -DHAVE_RB_THREAD_CALL_WITHOUT_GVL -DHAVE_RB_THREAD_FD_SELECT -DHAVE_TYPE_RB_FDSET_T -DHAVE_RB_WAIT_FOR_SINGLE_FD -DHAVE_RB_TIME_NEW -DHAVE_INOTIFY_INIT -DHAVE_INOTIFY -DHAVE_WRITEV -DHAVE_PIPE2 -DHAVE_ACCEPT4 -DHAVE_CONST_SOCK_CLOEXEC -DOS_UNIX -DHAVE_EPOLL_CREATE -DHAVE_EPOLL -DHAVE_CLOCK_GETTIME -DHAVE_CONST_CLOCK_MONOTONIC_RAW -DHAVE_CONST_CLOCK_MONOTONIC -DHAVE_MAKE_PAIR -Wdate-time -D_FORTIFY_SOURCE=3 $(DEFS) $(cppflags)
CXXFLAGS = $(CCDLFLAGS) -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=BUILDDIR=. -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection -fdebug-prefix-map=BUILDDIR=/usr/src/ruby3.2-3.2.3-1ubuntu0.24.04.8 $(ARCH_FLAG)
ldflags = -L. -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed
dldflags = -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now
ARCH_FLAG =
DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
LDSHARED = $(CXX) -shared
LDSHAREDXX = $(CXX) -shared
AR = x86_64-linux-gnu-gcc-ar
EXEEXT =
RUBY_INSTALL_NAME = $(RUBY_BASE_NAME)3.2
RUBY_SO_NAME = ruby-3.2
RUBYW_INSTALL_NAME =
RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version)
RUBYW_BASE_NAME = rubyw
RUBY_BASE_NAME = ruby
arch = x86_64-linux-gnu
sitearch = $(arch)
ruby_version = 3.2.0
ruby = $(bindir)/$(RUBY_BASE_NAME)3.2
RUBY = $(ruby)
BUILTRUBY = $(bindir)/$(RUBY_BASE_NAME)3.2
ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h
RM = rm -f
RM_RF = rm -fr
RMDIRS = rmdir --ignore-fail-on-non-empty -p
MAKEDIRS = /bin/mkdir -p
INSTALL = /usr/bin/install -c
INSTALL_PROG = $(INSTALL) -m 0755
INSTALL_DATA = $(INSTALL) -m 644
COPY = cp
TOUCH = exit >
#### End of system configuration section. ####
preload =
libpath = . $(archlibdir)
LIBPATH = -L. -L$(archlibdir)
DEFFILE =
CLEANFILES = mkmf.log
DISTCLEANFILES =
DISTCLEANDIRS =
extout =
extout_prefix =
target_prefix =
LOCAL_LIBS =
LIBS = $(LIBRUBYARG_SHARED) -lssl -lcrypto -lcrypto -lssl -lm -lpthread -lc
ORIG_SRCS = binder.cpp cmain.cpp ed.cpp em.cpp kb.cpp page.cpp pipe.cpp rubymain.cpp ssl.cpp
SRCS = $(ORIG_SRCS)
OBJS = binder.o cmain.o ed.o em.o kb.o page.o pipe.o rubymain.o ssl.o
HDRS = $(srcdir)/binder.h $(srcdir)/ed.h $(srcdir)/em.h $(srcdir)/eventmachine.h $(srcdir)/page.h $(srcdir)/project.h $(srcdir)/ssl.h
LOCAL_HDRS =
TARGET = rubyeventmachine
TARGET_NAME = rubyeventmachine
TARGET_ENTRY = Init_$(TARGET_NAME)
DLLIB = $(TARGET).so
EXTSTATIC =
STATIC_LIB =
TIMESTAMP_DIR = .
BINDIR = $(bindir)
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
RUBYLIBDIR = $(sitelibdir)$(target_prefix)
RUBYARCHDIR = $(sitearchdir)$(target_prefix)
HDRDIR = $(sitehdrdir)$(target_prefix)
ARCHHDRDIR = $(sitearchhdrdir)$(target_prefix)
TARGET_SO_DIR =
TARGET_SO = $(TARGET_SO_DIR)$(DLLIB)
CLEANLIBS = $(TARGET_SO) false
CLEANOBJS = $(OBJS) *.bak
TARGET_SO_DIR_TIMESTAMP = $(TIMESTAMP_DIR)/.sitearchdir.time
all: $(DLLIB)
static: $(STATIC_LIB)
.PHONY: all install static install-so install-rb
.PHONY: clean clean-so clean-static clean-rb
clean-static::
clean-rb-default::
clean-rb::
clean-so::
clean: clean-so clean-static clean-rb-default clean-rb
-$(Q)$(RM_RF) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time
distclean-rb-default::
distclean-rb::
distclean-so::
distclean-static::
distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
-$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
-$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
-$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true
realclean: distclean
install: install-so install-rb
install-so: $(DLLIB) $(TARGET_SO_DIR_TIMESTAMP)
$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
clean-static::
-$(Q)$(RM) $(STATIC_LIB)
install-rb: pre-install-rb do-install-rb install-rb-default
install-rb-default: pre-install-rb-default do-install-rb-default
pre-install-rb: Makefile
pre-install-rb-default: Makefile
do-install-rb:
do-install-rb-default:
pre-install-rb-default:
@$(NULLCMD)
$(TARGET_SO_DIR_TIMESTAMP):
$(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR)
$(Q) $(TOUCH) $@
site-install: site-install-so site-install-rb
site-install-so: install-so
site-install-rb: install-rb
.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S
.cc.o:
$(ECHO) compiling $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.cc.S:
$(ECHO) translating $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
.mm.o:
$(ECHO) compiling $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.mm.S:
$(ECHO) translating $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
.cxx.o:
$(ECHO) compiling $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.cxx.S:
$(ECHO) translating $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
.cpp.o:
$(ECHO) compiling $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.cpp.S:
$(ECHO) translating $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
.c.o:
$(ECHO) compiling $(<)
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.c.S:
$(ECHO) translating $(<)
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
.m.o:
$(ECHO) compiling $(<)
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.m.S:
$(ECHO) translating $(<)
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
$(TARGET_SO): $(OBJS) Makefile
$(ECHO) linking shared-object $(DLLIB)
-$(Q)$(RM) $(@)
$(Q) $(LDSHAREDXX) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
$(OBJS): $(HDRS) $(ruby_headers)
+124
View File
@@ -0,0 +1,124 @@
/*****************************************************************************
$Id$
File: binder.cpp
Date: 07Apr06
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
Gmail: blackhedd
This program is free software; you can redistribute it and/or modify
it under the terms of either: 1) 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; or 2) Ruby's License.
See the file COPYING for complete licensing information.
*****************************************************************************/
#include "project.h"
#define DEV_URANDOM "/dev/urandom"
std::map<uintptr_t, Bindable_t*> Bindable_t::BindingBag;
/********************************
STATIC Bindable_t::CreateBinding
********************************/
uintptr_t Bindable_t::CreateBinding()
{
static uintptr_t num = 0;
while(BindingBag[++num]) {}
return num;
}
#if 0
string Bindable_t::CreateBinding()
{
static int index = 0;
static string seed;
if ((index >= 1000000) || (seed.length() == 0)) {
#ifdef OS_UNIX
int fd = open (DEV_URANDOM, O_RDONLY);
if (fd < 0)
throw std::runtime_error ("No entropy device");
unsigned char u[16];
size_t r = read (fd, u, sizeof(u));
if (r < sizeof(u))
throw std::runtime_error ("Unable to read entropy device");
unsigned char *u1 = (unsigned char*)u;
char u2 [sizeof(u) * 2 + 1];
for (size_t i=0; i < sizeof(u); i++)
sprintf (u2 + (i * 2), "%02x", u1[i]);
seed = string (u2);
#endif
#ifdef OS_WIN32
UUID uuid;
UuidCreate (&uuid);
unsigned char *uuidstring = NULL;
UuidToString (&uuid, &uuidstring);
if (!uuidstring)
throw std::runtime_error ("Unable to read uuid");
seed = string ((const char*)uuidstring);
RpcStringFree (&uuidstring);
#endif
index = 0;
}
stringstream ss;
ss << seed << (++index);
return ss.str();
}
#endif
/*****************************
STATIC: Bindable_t::GetObject
*****************************/
Bindable_t *Bindable_t::GetObject (const uintptr_t binding)
{
std::map<uintptr_t, Bindable_t*>::const_iterator i = BindingBag.find (binding);
if (i != BindingBag.end())
return i->second;
else
return NULL;
}
/**********************
Bindable_t::Bindable_t
**********************/
Bindable_t::Bindable_t()
{
Binding = Bindable_t::CreateBinding();
BindingBag [Binding] = this;
}
/***********************
Bindable_t::~Bindable_t
***********************/
Bindable_t::~Bindable_t() NO_EXCEPT_FALSE
{
BindingBag.erase (Binding);
}
+52
View File
@@ -0,0 +1,52 @@
/*****************************************************************************
$Id$
File: binder.h
Date: 07Apr06
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
Gmail: blackhedd
This program is free software; you can redistribute it and/or modify
it under the terms of either: 1) 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; or 2) Ruby's License.
See the file COPYING for complete licensing information.
*****************************************************************************/
#ifndef __ObjectBindings__H_
#define __ObjectBindings__H_
#if __cplusplus >= 201103L
#define NO_EXCEPT_FALSE noexcept(false)
#else
#define NO_EXCEPT_FALSE
#endif
class Bindable_t
{
public:
static uintptr_t CreateBinding();
static Bindable_t *GetObject (const uintptr_t);
static std::map<uintptr_t, Bindable_t*> BindingBag;
public:
Bindable_t();
virtual ~Bindable_t() NO_EXCEPT_FALSE;
const uintptr_t GetBinding() {return Binding;}
private:
uintptr_t Binding;
};
#endif // __ObjectBindings__H_
+988
View File
@@ -0,0 +1,988 @@
/*****************************************************************************
$Id$
File: cmain.cpp
Date: 06Apr06
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
Gmail: blackhedd
This program is free software; you can redistribute it and/or modify
it under the terms of either: 1) 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; or 2) Ruby's License.
See the file COPYING for complete licensing information.
*****************************************************************************/
#include "project.h"
/* 21Sep09: ruby 1.9 defines macros for common i/o functions that point to rb_w32_* implementations.
We need to undef the stat to fix a build failure in evma_send_file_data_to_connection.
See http://groups.google.com/group/eventmachine/browse_thread/thread/fc60d9bb738ffc71
*/
#if defined(BUILD_FOR_RUBY) && defined(OS_WIN32)
#undef stat
#undef fstat
#endif
static EventMachine_t *EventMachine;
static Poller_t Poller = Poller_Default;
extern "C" void ensure_eventmachine (const char *caller = "unknown caller")
{
if (!EventMachine) {
const int err_size = 128;
char err_string[err_size];
snprintf (err_string, err_size, "eventmachine not initialized: %s", caller);
#ifdef BUILD_FOR_RUBY
rb_raise(rb_eRuntimeError, "%s", err_string);
#else
throw std::runtime_error (err_string);
#endif
}
}
/***********************
evma_initialize_library
***********************/
extern "C" void evma_initialize_library (EMCallback cb)
{
if (EventMachine)
#ifdef BUILD_FOR_RUBY
rb_raise(rb_eRuntimeError, "eventmachine already initialized: evma_initialize_library");
#else
throw std::runtime_error ("eventmachine already initialized: evma_initialize_library");
#endif
EventMachine = new EventMachine_t (cb, Poller);
}
/********************
evma_release_library
********************/
extern "C" void evma_release_library()
{
ensure_eventmachine("evma_release_library");
delete EventMachine;
EventMachine = NULL;
}
/*********************
evma_run_machine_once
*********************/
extern "C" bool evma_run_machine_once()
{
ensure_eventmachine("evma_run_machine_once");
return EventMachine->RunOnce();
}
/****************
evma_run_machine
****************/
extern "C" void evma_run_machine()
{
ensure_eventmachine("evma_run_machine");
EventMachine->Run();
}
/**************************
evma_install_oneshot_timer
**************************/
extern "C" const uintptr_t evma_install_oneshot_timer (uint64_t milliseconds)
{
ensure_eventmachine("evma_install_oneshot_timer");
return EventMachine->InstallOneshotTimer (milliseconds);
}
/**********************
evma_connect_to_server
**********************/
extern "C" const uintptr_t evma_connect_to_server (const char *bind_addr, int bind_port, const char *server, int port)
{
ensure_eventmachine("evma_connect_to_server");
return EventMachine->ConnectToServer (bind_addr, bind_port, server, port);
}
/***************************
evma_connect_to_unix_server
***************************/
extern "C" const uintptr_t evma_connect_to_unix_server (const char *server)
{
ensure_eventmachine("evma_connect_to_unix_server");
return EventMachine->ConnectToUnixServer (server);
}
/**************
evma_attach_fd
**************/
extern "C" const uintptr_t evma_attach_fd (int file_descriptor, int watch_mode)
{
ensure_eventmachine("evma_attach_fd");
return EventMachine->AttachFD (file_descriptor, watch_mode ? true : false);
}
/**************
evma_detach_fd
**************/
extern "C" int evma_detach_fd (const uintptr_t binding)
{
ensure_eventmachine("evma_detach_fd");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (ed)
return EventMachine->DetachFD (ed);
else
#ifdef BUILD_FOR_RUBY
rb_raise(rb_eRuntimeError, "invalid binding to detach");
#else
throw std::runtime_error ("invalid binding to detach");
#endif
return -1;
}
/************************
evma_get_file_descriptor
************************/
extern "C" int evma_get_file_descriptor (const uintptr_t binding)
{
ensure_eventmachine("evma_get_file_descriptor");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (ed)
return ed->GetSocket();
else
#ifdef BUILD_FOR_RUBY
rb_raise(rb_eRuntimeError, "invalid binding to get_fd");
#else
throw std::runtime_error ("invalid binding to get_fd");
#endif
return -1;
}
/***********************
evma_is_notify_readable
***********************/
extern "C" int evma_is_notify_readable (const uintptr_t binding)
{
ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
if (cd)
return cd->IsNotifyReadable() ? 1 : 0;
return -1;
}
/************************
evma_set_notify_readable
************************/
extern "C" void evma_set_notify_readable (const uintptr_t binding, int mode)
{
ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
if (cd)
cd->SetNotifyReadable (mode ? true : false);
}
/***********************
evma_is_notify_writable
***********************/
extern "C" int evma_is_notify_writable (const uintptr_t binding)
{
ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
if (cd)
return cd->IsNotifyWritable() ? 1 : 0;
return -1;
}
/************************
evma_set_notify_writable
************************/
extern "C" void evma_set_notify_writable (const uintptr_t binding, int mode)
{
ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
if (cd)
cd->SetNotifyWritable (mode ? true : false);
}
/**********
evma_pause
**********/
extern "C" int evma_pause (const uintptr_t binding)
{
EventableDescriptor *cd = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (cd)
return cd->Pause() ? 1 : 0;
return 0;
}
/***********
evma_resume
***********/
extern "C" int evma_resume (const uintptr_t binding)
{
EventableDescriptor *cd = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (cd)
return cd->Resume() ? 1 : 0;
return 0;
}
/**************
evma_is_paused
**************/
extern "C" int evma_is_paused (const uintptr_t binding)
{
EventableDescriptor *cd = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (cd)
return cd->IsPaused() ? 1 : 0;
return 0;
}
/************************
evma_num_close_scheduled
************************/
extern "C" int evma_num_close_scheduled ()
{
ensure_eventmachine("evma_num_close_scheduled");
return EventMachine->NumCloseScheduled;
}
/**********************
evma_create_tcp_server
**********************/
extern "C" const uintptr_t evma_create_tcp_server (const char *address, int port)
{
ensure_eventmachine("evma_create_tcp_server");
return EventMachine->CreateTcpServer (address, port);
}
/******************************
evma_create_unix_domain_server
******************************/
extern "C" const uintptr_t evma_create_unix_domain_server (const char *filename)
{
ensure_eventmachine("evma_create_unix_domain_server");
return EventMachine->CreateUnixDomainServer (filename);
}
/***********************
evma_attach_sd
************************/
extern "C" const uintptr_t evma_attach_sd (int sd)
{
ensure_eventmachine("evma_attach_sd");
return EventMachine->AttachSD (sd);
}
/*************************
evma_open_datagram_socket
*************************/
extern "C" const uintptr_t evma_open_datagram_socket (const char *address, int port)
{
ensure_eventmachine("evma_open_datagram_socket");
return EventMachine->OpenDatagramSocket (address, port);
}
/******************
evma_open_keyboard
******************/
extern "C" const uintptr_t evma_open_keyboard()
{
ensure_eventmachine("evma_open_keyboard");
return EventMachine->OpenKeyboard();
}
/*******************
evma_watch_filename
*******************/
extern "C" const uintptr_t evma_watch_filename (const char *fname)
{
ensure_eventmachine("evma_watch_filename");
return EventMachine->WatchFile(fname);
}
/*********************
evma_unwatch_filename
*********************/
extern "C" void evma_unwatch_filename (const uintptr_t sig)
{
ensure_eventmachine("evma_unwatch_file");
EventMachine->UnwatchFile(sig);
}
/**************
evma_watch_pid
**************/
extern "C" const uintptr_t evma_watch_pid (int pid)
{
ensure_eventmachine("evma_watch_pid");
return EventMachine->WatchPid(pid);
}
/****************
evma_unwatch_pid
****************/
extern "C" void evma_unwatch_pid (const uintptr_t sig)
{
ensure_eventmachine("evma_unwatch_pid");
EventMachine->UnwatchPid(sig);
}
/****************************
evma_send_data_to_connection
****************************/
extern "C" int evma_send_data_to_connection (const uintptr_t binding, const char *data, int data_length)
{
ensure_eventmachine("evma_send_data_to_connection");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (ed)
return ed->SendOutboundData(data, data_length);
return -1;
}
/******************
evma_send_datagram
******************/
extern "C" int evma_send_datagram (const uintptr_t binding, const char *data, int data_length, const char *address, int port)
{
ensure_eventmachine("evma_send_datagram");
DatagramDescriptor *dd = dynamic_cast <DatagramDescriptor*> (Bindable_t::GetObject (binding));
if (dd)
return dd->SendOutboundDatagram(data, data_length, address, port);
return -1;
}
/*********************
evma_close_connection
*********************/
extern "C" void evma_close_connection (const uintptr_t binding, int after_writing)
{
ensure_eventmachine("evma_close_connection");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (ed)
ed->ScheduleClose (after_writing ? true : false);
}
/***********************************
evma_report_connection_error_status
***********************************/
extern "C" int evma_report_connection_error_status (const uintptr_t binding)
{
ensure_eventmachine("evma_report_connection_error_status");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (ed)
return ed->ReportErrorStatus();
return -1;
}
/********************
evma_stop_tcp_server
********************/
extern "C" void evma_stop_tcp_server (const uintptr_t binding)
{
ensure_eventmachine("evma_stop_tcp_server");
AcceptorDescriptor::StopAcceptor (binding);
}
/*****************
evma_stop_machine
*****************/
extern "C" void evma_stop_machine()
{
ensure_eventmachine("evma_stop_machine");
EventMachine->ScheduleHalt();
}
/*****************
evma_stopping
*****************/
extern "C" bool evma_stopping()
{
ensure_eventmachine("evma_stopping");
return EventMachine->Stopping();
}
/**************
evma_start_tls
**************/
extern "C" void evma_start_tls (const uintptr_t binding)
{
ensure_eventmachine("evma_start_tls");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (ed)
ed->StartTls();
}
/******************
evma_set_tls_parms
******************/
extern "C" void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer, int fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int ssl_version)
{
ensure_eventmachine("evma_set_tls_parms");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (ed)
ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false), (fail_if_no_peer_cert == 1 ? true : false), sni_hostname, cipherlist, ecdh_curve, dhparam, ssl_version);
}
/******************
evma_get_peer_cert
******************/
#ifdef WITH_SSL
extern "C" X509 *evma_get_peer_cert (const uintptr_t binding)
{
ensure_eventmachine("evma_get_peer_cert");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (ed)
return ed->GetPeerCert();
return NULL;
}
#endif
/******************
evma_get_cipher_bits
******************/
#ifdef WITH_SSL
extern "C" int evma_get_cipher_bits (const uintptr_t binding)
{
ensure_eventmachine("evma_get_cipher_bits");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (ed)
return ed->GetCipherBits();
return -1;
}
#endif
/******************
evma_get_cipher_name
******************/
#ifdef WITH_SSL
extern "C" const char *evma_get_cipher_name (const uintptr_t binding)
{
ensure_eventmachine("evma_get_cipher_name");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (ed)
return ed->GetCipherName();
return NULL;
}
#endif
/******************
evma_get_cipher_protocol
******************/
#ifdef WITH_SSL
extern "C" const char *evma_get_cipher_protocol (const uintptr_t binding)
{
ensure_eventmachine("evma_get_cipher_protocol");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (ed)
return ed->GetCipherProtocol();
return NULL;
}
#endif
/******************
evma_get_sni_hostname
******************/
#ifdef WITH_SSL
extern "C" const char *evma_get_sni_hostname (const uintptr_t binding)
{
ensure_eventmachine("evma_get_sni_hostname");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (ed)
return ed->GetSNIHostname();
return NULL;
}
#endif
/********************
evma_accept_ssl_peer
********************/
#ifdef WITH_SSL
extern "C" void evma_accept_ssl_peer (const uintptr_t binding)
{
ensure_eventmachine("evma_accept_ssl_peer");
ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
if (cd)
cd->AcceptSslPeer();
}
#endif
/*****************
evma_get_peername
*****************/
extern "C" int evma_get_peername (const uintptr_t binding, struct sockaddr *sa, socklen_t *len)
{
ensure_eventmachine("evma_get_peername");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (ed) {
return ed->GetPeername (sa, len) ? 1 : 0;
}
else
return 0;
}
/*****************
evma_get_sockname
*****************/
extern "C" int evma_get_sockname (const uintptr_t binding, struct sockaddr *sa, socklen_t *len)
{
ensure_eventmachine("evma_get_sockname");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (ed) {
return ed->GetSockname (sa, len) ? 1 : 0;
}
else
return 0;
}
/***********************
evma_get_subprocess_pid
***********************/
#ifdef OS_UNIX
extern "C" int evma_get_subprocess_pid (const uintptr_t binding, pid_t *pid)
{
ensure_eventmachine("evma_get_subprocess_pid");
PipeDescriptor *pd = dynamic_cast <PipeDescriptor*> (Bindable_t::GetObject (binding));
if (pd) {
return pd->GetSubprocessPid (pid) ? 1 : 0;
}
else if (pid && EventMachine->SubprocessPid) {
*pid = EventMachine->SubprocessPid;
return 1;
}
else
return 0;
}
#else
extern "C" int evma_get_subprocess_pid (const uintptr_t binding UNUSED, pid_t *pid UNUSED)
{
return 0;
}
#endif
/**************************
evma_get_subprocess_status
**************************/
extern "C" int evma_get_subprocess_status (const uintptr_t binding UNUSED, int *status)
{
ensure_eventmachine("evma_get_subprocess_status");
if (status) {
*status = EventMachine->SubprocessExitStatus;
return 1;
}
else
return 0;
}
/*************************
evma_get_connection_count
*************************/
extern "C" int evma_get_connection_count()
{
ensure_eventmachine("evma_get_connection_count");
return EventMachine->GetConnectionCount();
}
/*********************
evma_signal_loopbreak
*********************/
extern "C" void evma_signal_loopbreak()
{
ensure_eventmachine("evma_signal_loopbreak");
EventMachine->SignalLoopBreaker();
}
/********************************
evma_get_comm_inactivity_timeout
********************************/
extern "C" float evma_get_comm_inactivity_timeout (const uintptr_t binding)
{
ensure_eventmachine("evma_get_comm_inactivity_timeout");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (ed) {
return ((float)ed->GetCommInactivityTimeout() / 1000);
}
else
return 0.0; //Perhaps this should be an exception. Access to an unknown binding.
}
/********************************
evma_set_comm_inactivity_timeout
********************************/
extern "C" int evma_set_comm_inactivity_timeout (const uintptr_t binding, float value)
{
ensure_eventmachine("evma_set_comm_inactivity_timeout");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (ed) {
return ed->SetCommInactivityTimeout ((uint64_t)(value * 1000));
}
else
return 0; //Perhaps this should be an exception. Access to an unknown binding.
}
/********************************
evma_get_pending_connect_timeout
********************************/
extern "C" float evma_get_pending_connect_timeout (const uintptr_t binding)
{
ensure_eventmachine("evma_get_pending_connect_timeout");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (ed) {
return ((float)ed->GetPendingConnectTimeout() / 1000);
}
else
return 0.0;
}
/********************************
evma_set_pending_connect_timeout
********************************/
extern "C" int evma_set_pending_connect_timeout (const uintptr_t binding, float value)
{
ensure_eventmachine("evma_set_pending_connect_timeout");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (ed) {
return ed->SetPendingConnectTimeout ((uint64_t)(value * 1000));
}
else
return 0;
}
/**********************
evma_set_timer_quantum
**********************/
extern "C" void evma_set_timer_quantum (int interval)
{
ensure_eventmachine("evma_set_timer_quantum");
EventMachine->SetTimerQuantum (interval);
}
/************************
evma_get_max_timer_count
************************/
extern "C" int evma_get_max_timer_count()
{
return EventMachine_t::GetMaxTimerCount();
}
/************************
evma_set_max_timer_count
************************/
extern "C" void evma_set_max_timer_count (int ct)
{
// This may only be called if the reactor is not running.
if (EventMachine)
#ifdef BUILD_FOR_RUBY
rb_raise(rb_eRuntimeError, "eventmachine already initialized: evma_set_max_timer_count");
#else
throw std::runtime_error ("eventmachine already initialized: evma_set_max_timer_count");
#endif
EventMachine_t::SetMaxTimerCount (ct);
}
/******************
evma_get/set_simultaneous_accept_count
******************/
extern "C" void evma_set_simultaneous_accept_count (int count)
{
EventMachine_t::SetSimultaneousAcceptCount(count);
}
extern "C" int evma_get_simultaneous_accept_count()
{
return EventMachine_t::GetSimultaneousAcceptCount();
}
/******************
evma_setuid_string
******************/
extern "C" void evma_setuid_string (const char *username)
{
// We do NOT need to be running an EM instance because this method is static.
EventMachine_t::SetuidString (username);
}
/**********
evma_popen
**********/
extern "C" const uintptr_t evma_popen (char * const*cmd_strings)
{
ensure_eventmachine("evma_popen");
return EventMachine->Socketpair (cmd_strings);
}
/***************************
evma_get_outbound_data_size
***************************/
extern "C" int evma_get_outbound_data_size (const uintptr_t binding)
{
ensure_eventmachine("evma_get_outbound_data_size");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
return ed ? ed->GetOutboundDataSize() : 0;
}
/**************
evma_set_epoll
**************/
extern "C" void evma_set_epoll (int use)
{
if (use)
Poller = Poller_Epoll;
else
Poller = Poller_Default;
}
/***************
evma_set_kqueue
***************/
extern "C" void evma_set_kqueue (int use)
{
if (use)
Poller = Poller_Kqueue;
else
Poller = Poller_Default;
}
/**********************
evma_set_rlimit_nofile
**********************/
extern "C" int evma_set_rlimit_nofile (int nofiles)
{
return EventMachine_t::SetRlimitNofile (nofiles);
}
/*********************************
evma_send_file_data_to_connection
*********************************/
extern "C" int evma_send_file_data_to_connection (const uintptr_t binding, const char *filename)
{
/* This is a sugaring over send_data_to_connection that reads a file into a
* locally-allocated buffer, and sends the file data to the remote peer.
* Return the number of bytes written to the caller.
* TODO, needs to impose a limit on the file size. This is intended only for
* small files. (I don't know, maybe 8K or less.) For larger files, use interleaved
* I/O to avoid slowing the rest of the system down.
* TODO: we should return a code rather than barf, in case of file-not-found.
* TODO, does this compile on Windows?
* TODO, given that we want this to work only with small files, how about allocating
* the buffer on the stack rather than the heap?
*
* Modified 25Jul07. This now returns -1 on file-too-large; 0 for success, and a positive
* errno in case of other errors.
*
* Contributed by Kirk Haines.
*/
char data[32*1024];
int r;
ensure_eventmachine("evma_send_file_data_to_connection");
#if defined(OS_WIN32)
int Fd = open (filename, O_RDONLY|O_BINARY);
#else
int Fd = open (filename, O_RDONLY);
#endif
if (Fd < 0)
return errno;
// From here on, all early returns MUST close Fd.
struct stat st;
if (fstat (Fd, &st)) {
int e = errno;
close (Fd);
return e;
}
off_t filesize = st.st_size;
if (filesize <= 0) {
close (Fd);
return 0;
}
else if (filesize > (off_t) sizeof(data)) {
close (Fd);
return -1;
}
r = read (Fd, data, filesize);
if (r != filesize) {
int e = errno;
close (Fd);
return e;
}
evma_send_data_to_connection (binding, data, r);
close (Fd);
return 0;
}
/****************
evma_start_proxy
*****************/
extern "C" void evma_start_proxy (const uintptr_t from, const uintptr_t to, const unsigned long bufsize, const unsigned long length)
{
ensure_eventmachine("evma_start_proxy");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (from));
if (ed)
ed->StartProxy(to, bufsize, length);
}
/***************
evma_stop_proxy
****************/
extern "C" void evma_stop_proxy (const uintptr_t from)
{
ensure_eventmachine("evma_stop_proxy");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (from));
if (ed)
ed->StopProxy();
}
/******************
evma_proxied_bytes
*******************/
extern "C" unsigned long evma_proxied_bytes (const uintptr_t from)
{
ensure_eventmachine("evma_proxied_bytes");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (from));
if (ed)
return ed->GetProxiedBytes();
else
return 0;
}
/***************************
evma_get_last_activity_time
****************************/
extern "C" uint64_t evma_get_last_activity_time(const uintptr_t from)
{
ensure_eventmachine("evma_get_last_activity_time");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (from));
if (ed)
return ed->GetLastActivity();
else
return 0;
}
/***************************
evma_get_heartbeat_interval
****************************/
extern "C" float evma_get_heartbeat_interval()
{
ensure_eventmachine("evma_get_heartbeat_interval");
return EventMachine->GetHeartbeatInterval();
}
/***************************
evma_set_heartbeat_interval
****************************/
extern "C" int evma_set_heartbeat_interval(float interval)
{
ensure_eventmachine("evma_set_heartbeat_interval");
return EventMachine->SetHeartbeatInterval(interval);
}
/**************************
evma_get_current_loop_time
***************************/
extern "C" uint64_t evma_get_current_loop_time()
{
ensure_eventmachine("evma_get_current_loop_time");
return EventMachine->GetCurrentLoopTime();
}
File diff suppressed because it is too large Load Diff
+457
View File
@@ -0,0 +1,457 @@
/*****************************************************************************
$Id$
File: ed.h
Date: 06Apr06
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
Gmail: blackhedd
This program is free software; you can redistribute it and/or modify
it under the terms of either: 1) 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; or 2) Ruby's License.
See the file COPYING for complete licensing information.
*****************************************************************************/
#ifndef __EventableDescriptor__H_
#define __EventableDescriptor__H_
class EventMachine_t; // forward reference
#ifdef WITH_SSL
class SslBox_t; // forward reference
#endif
bool SetSocketNonblocking (SOCKET);
bool SetFdCloexec (int);
/*************************
class EventableDescriptor
*************************/
class EventableDescriptor: public Bindable_t
{
public:
EventableDescriptor (SOCKET, EventMachine_t*);
virtual ~EventableDescriptor() NO_EXCEPT_FALSE;
SOCKET GetSocket() {return MySocket;}
void SetSocketInvalid() { MySocket = INVALID_SOCKET; }
void Close();
virtual void Read() = 0;
virtual void Write() = 0;
virtual void Heartbeat() = 0;
// These methods tell us whether the descriptor
// should be selected or polled for read/write.
virtual bool SelectForRead() = 0;
virtual bool SelectForWrite() = 0;
// are we scheduled for a close, or in an error state, or already closed?
bool ShouldDelete();
// Do we have any data to write? This is used by ShouldDelete.
virtual int GetOutboundDataSize() {return 0;}
virtual bool IsWatchOnly(){ return bWatchOnly; }
virtual void ScheduleClose (bool after_writing);
bool IsCloseScheduled();
virtual void HandleError(){ ScheduleClose (false); }
void SetEventCallback (EMCallback);
virtual bool GetPeername (struct sockaddr*, socklen_t*) = 0;
virtual bool GetSockname (struct sockaddr*, socklen_t*) = 0;
virtual bool GetSubprocessPid (pid_t*) {return false;}
virtual void StartTls() {}
virtual void SetTlsParms (const char *, const char *, bool, bool, const char *, const char *, const char *, const char *, int) {}
#ifdef WITH_SSL
virtual X509 *GetPeerCert() {return NULL;}
virtual int GetCipherBits() {return -1;}
virtual const char *GetCipherName() {return NULL;}
virtual const char *GetCipherProtocol() {return NULL;}
virtual const char *GetSNIHostname() {return NULL;}
#endif
virtual uint64_t GetCommInactivityTimeout() {return 0;}
virtual int SetCommInactivityTimeout (uint64_t) {return 0;}
uint64_t GetPendingConnectTimeout();
int SetPendingConnectTimeout (uint64_t value);
uint64_t GetLastActivity() { return LastActivity; }
#ifdef HAVE_EPOLL
struct epoll_event *GetEpollEvent() { return &EpollEvent; }
#endif
#ifdef HAVE_KQUEUE
bool GetKqueueArmWrite() { return bKqueueArmWrite; }
#endif
virtual void StartProxy(const uintptr_t, const unsigned long, const unsigned long);
virtual void StopProxy();
virtual unsigned long GetProxiedBytes(){ return ProxiedBytes; };
virtual void SetProxiedFrom(EventableDescriptor*, const unsigned long);
virtual int SendOutboundData(const char*,unsigned long){ return -1; }
virtual bool IsPaused(){ return bPaused; }
virtual bool Pause(){ bPaused = true; return bPaused; }
virtual bool Resume(){ bPaused = false; return bPaused; }
void SetUnbindReasonCode(int code){ UnbindReasonCode = code; }
virtual int ReportErrorStatus(){ return 0; }
virtual bool IsConnectPending(){ return false; }
virtual uint64_t GetNextHeartbeat();
private:
bool bCloseNow;
bool bCloseAfterWriting;
protected:
SOCKET MySocket;
bool bAttached;
bool bWatchOnly;
EMCallback EventCallback;
void _GenericInboundDispatch (const char *buffer, unsigned long size);
bool _GenericGetPeername (struct sockaddr*, socklen_t*);
bool _GenericGetSockname (struct sockaddr*, socklen_t*);
uint64_t CreatedAt;
bool bCallbackUnbind;
int UnbindReasonCode;
unsigned long BytesToProxy;
EventableDescriptor *ProxyTarget;
EventableDescriptor *ProxiedFrom;
unsigned long ProxiedBytes;
unsigned long MaxOutboundBufSize;
#ifdef HAVE_EPOLL
struct epoll_event EpollEvent;
#endif
#ifdef HAVE_KQUEUE
bool bKqueueArmWrite;
#endif
EventMachine_t *MyEventMachine;
uint64_t PendingConnectTimeout;
uint64_t InactivityTimeout;
uint64_t LastActivity;
uint64_t NextHeartbeat;
bool bPaused;
};
/*************************
class LoopbreakDescriptor
*************************/
class LoopbreakDescriptor: public EventableDescriptor
{
public:
LoopbreakDescriptor (SOCKET, EventMachine_t*);
virtual ~LoopbreakDescriptor() {}
virtual void Read();
virtual void Write();
virtual void Heartbeat() {}
virtual bool SelectForRead() {return true;}
virtual bool SelectForWrite() {return false;}
virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return _GenericGetPeername (s, len); }
virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); }
};
/**************************
class ConnectionDescriptor
**************************/
class ConnectionDescriptor: public EventableDescriptor
{
public:
ConnectionDescriptor (SOCKET, EventMachine_t*);
virtual ~ConnectionDescriptor();
int SendOutboundData (const char*, unsigned long);
void SetConnectPending (bool f);
virtual void ScheduleClose (bool after_writing);
virtual void HandleError();
void SetNotifyReadable (bool);
void SetNotifyWritable (bool);
void SetAttached (bool);
void SetWatchOnly (bool);
bool Pause();
bool Resume();
bool IsNotifyReadable(){ return bNotifyReadable; }
bool IsNotifyWritable(){ return bNotifyWritable; }
virtual void Read();
virtual void Write();
virtual void Heartbeat();
virtual bool SelectForRead();
virtual bool SelectForWrite();
// Do we have any data to write? This is used by ShouldDelete.
virtual int GetOutboundDataSize() {return OutboundDataSize;}
virtual void StartTls();
virtual void SetTlsParms (const char *, const char *, bool, bool, const char *, const char *, const char *, const char *, int);
#ifdef WITH_SSL
virtual X509 *GetPeerCert();
virtual int GetCipherBits();
virtual const char *GetCipherName();
virtual const char *GetCipherProtocol();
virtual const char *GetSNIHostname();
virtual bool VerifySslPeer(const char*);
virtual void AcceptSslPeer();
#endif
void SetServerMode() {bIsServer = true;}
virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return _GenericGetPeername (s, len); }
virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); }
virtual uint64_t GetCommInactivityTimeout();
virtual int SetCommInactivityTimeout (uint64_t value);
virtual int ReportErrorStatus();
virtual bool IsConnectPending(){ return bConnectPending; }
protected:
struct OutboundPage {
OutboundPage (const char *b, int l, int o=0): Buffer(b), Length(l), Offset(o) {}
void Free() {if (Buffer) free (const_cast<char*>(Buffer)); }
const char *Buffer;
int Length;
int Offset;
};
protected:
bool bConnectPending;
bool bNotifyReadable;
bool bNotifyWritable;
bool bReadAttemptedAfterClose;
bool bWriteAttemptedAfterClose;
std::deque<OutboundPage> OutboundPages;
int OutboundDataSize;
#ifdef WITH_SSL
SslBox_t *SslBox;
std::string CertChainFilename;
std::string PrivateKeyFilename;
std::string CipherList;
std::string EcdhCurve;
std::string DhParam;
int Protocols;
bool bHandshakeSignaled;
bool bSslVerifyPeer;
bool bSslFailIfNoPeerCert;
std::string SniHostName;
bool bSslPeerAccepted;
#endif
#ifdef HAVE_KQUEUE
bool bGotExtraKqueueEvent;
#endif
bool bIsServer;
private:
void _UpdateEvents();
void _UpdateEvents(bool, bool);
void _WriteOutboundData();
void _DispatchInboundData (const char *buffer, unsigned long size);
void _DispatchCiphertext();
int _SendRawOutboundData (const char *buffer, unsigned long size);
void _CheckHandshakeStatus();
};
/************************
class DatagramDescriptor
************************/
class DatagramDescriptor: public EventableDescriptor
{
public:
DatagramDescriptor (SOCKET, EventMachine_t*);
virtual ~DatagramDescriptor();
virtual void Read();
virtual void Write();
virtual void Heartbeat();
virtual bool SelectForRead() {return true;}
virtual bool SelectForWrite();
int SendOutboundData (const char*, unsigned long);
int SendOutboundDatagram (const char*, unsigned long, const char*, int);
// Do we have any data to write? This is used by ShouldDelete.
virtual int GetOutboundDataSize() {return OutboundDataSize;}
virtual bool GetPeername (struct sockaddr* s, socklen_t* len);
virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); };
virtual uint64_t GetCommInactivityTimeout();
virtual int SetCommInactivityTimeout (uint64_t value);
protected:
struct OutboundPage {
OutboundPage (const char *b, int l, struct sockaddr_in6 f, int o=0): Buffer(b), Length(l), Offset(o), From(f) {}
void Free() {if (Buffer) free (const_cast<char*>(Buffer)); }
const char *Buffer;
int Length;
int Offset;
struct sockaddr_in6 From;
};
std::deque<OutboundPage> OutboundPages;
int OutboundDataSize;
struct sockaddr_in6 ReturnAddress;
};
/************************
class AcceptorDescriptor
************************/
class AcceptorDescriptor: public EventableDescriptor
{
public:
AcceptorDescriptor (SOCKET, EventMachine_t*);
virtual ~AcceptorDescriptor();
virtual void Read();
virtual void Write();
virtual void Heartbeat();
virtual bool SelectForRead() {return true;}
virtual bool SelectForWrite() {return false;}
virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return _GenericGetPeername (s, len); }
virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); };
static void StopAcceptor (const uintptr_t binding);
};
/********************
class PipeDescriptor
********************/
#ifdef OS_UNIX
class PipeDescriptor: public EventableDescriptor
{
public:
PipeDescriptor (SOCKET, pid_t, EventMachine_t*);
virtual ~PipeDescriptor() NO_EXCEPT_FALSE;
virtual void Read();
virtual void Write();
virtual void Heartbeat();
virtual bool SelectForRead();
virtual bool SelectForWrite();
int SendOutboundData (const char*, unsigned long);
virtual int GetOutboundDataSize() {return OutboundDataSize;}
virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return _GenericGetPeername (s, len); }
virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); }
virtual bool GetSubprocessPid (pid_t*);
protected:
struct OutboundPage {
OutboundPage (const char *b, int l, int o=0): Buffer(b), Length(l), Offset(o) {}
void Free() {if (Buffer) free (const_cast<char*>(Buffer)); }
const char *Buffer;
int Length;
int Offset;
};
protected:
bool bReadAttemptedAfterClose;
std::deque<OutboundPage> OutboundPages;
int OutboundDataSize;
pid_t SubprocessPid;
private:
void _DispatchInboundData (const char *buffer, int size);
};
#endif // OS_UNIX
/************************
class KeyboardDescriptor
************************/
class KeyboardDescriptor: public EventableDescriptor
{
public:
KeyboardDescriptor (EventMachine_t*);
virtual ~KeyboardDescriptor();
virtual void Read();
virtual void Write();
virtual void Heartbeat();
virtual bool SelectForRead() {return true;}
virtual bool SelectForWrite() {return false;}
virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return _GenericGetPeername (s, len); }
virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); }
protected:
bool bReadAttemptedAfterClose;
private:
void _DispatchInboundData (const char *buffer, int size);
};
/***********************
class InotifyDescriptor
************************/
class InotifyDescriptor: public EventableDescriptor
{
public:
InotifyDescriptor (EventMachine_t*);
virtual ~InotifyDescriptor();
void Read();
void Write();
virtual void Heartbeat() {}
virtual bool SelectForRead() {return true;}
virtual bool SelectForWrite() {return false;}
virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return false; }
virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return false; }
};
#endif // __EventableDescriptor__H_
File diff suppressed because it is too large Load Diff
+308
View File
@@ -0,0 +1,308 @@
/*****************************************************************************
$Id$
File: em.h
Date: 06Apr06
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
Gmail: blackhedd
This program is free software; you can redistribute it and/or modify
it under the terms of either: 1) 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; or 2) Ruby's License.
See the file COPYING for complete licensing information.
*****************************************************************************/
#ifndef __EventMachine__H_
#define __EventMachine__H_
#ifdef BUILD_FOR_RUBY
#include <ruby.h>
#ifdef HAVE_RB_THREAD_FD_SELECT
#define EmSelect rb_thread_fd_select
#else
// ruby 1.9.1 and below
#define EmSelect rb_thread_select
#endif
#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
#include <ruby/thread.h>
#endif
#ifdef HAVE_RB_WAIT_FOR_SINGLE_FD
#include <ruby/io.h>
#endif
#if defined(HAVE_RB_TRAP_IMMEDIATE)
#include <rubysig.h>
#elif defined(HAVE_RB_ENABLE_INTERRUPT)
extern "C" {
void rb_enable_interrupt(void);
void rb_disable_interrupt(void);
}
#define TRAP_BEG rb_enable_interrupt()
#define TRAP_END do { rb_disable_interrupt(); rb_thread_check_ints(); } while(0)
#else
#define TRAP_BEG
#define TRAP_END
#endif
// 1.9.0 compat
#ifndef RUBY_UBF_IO
#define RUBY_UBF_IO RB_UBF_DFL
#endif
#ifndef RSTRING_PTR
#define RSTRING_PTR(str) RSTRING(str)->ptr
#endif
#ifndef RSTRING_LEN
#define RSTRING_LEN(str) RSTRING(str)->len
#endif
#ifndef RSTRING_LENINT
#define RSTRING_LENINT(str) RSTRING_LEN(str)
#endif
#else
#define EmSelect select
#endif
#if !defined(HAVE_TYPE_RB_FDSET_T)
#define fd_check(n) (((n) < FD_SETSIZE) ? 1 : 0*fprintf(stderr, "fd %d too large for select\n", (n)))
// These definitions are cribbed from include/ruby/intern.h in Ruby 1.9.3,
// with this change: any macros that read or write the nth element of an
// fdset first call fd_check to make sure n is in bounds.
typedef fd_set rb_fdset_t;
#define rb_fd_zero(f) FD_ZERO(f)
#define rb_fd_set(n, f) do { if (fd_check(n)) FD_SET((n), (f)); } while(0)
#define rb_fd_clr(n, f) do { if (fd_check(n)) FD_CLR((n), (f)); } while(0)
#define rb_fd_isset(n, f) (fd_check(n) ? FD_ISSET((n), (f)) : 0)
#define rb_fd_copy(d, s, n) (*(d) = *(s))
#define rb_fd_dup(d, s) (*(d) = *(s))
#define rb_fd_resize(n, f) ((void)(f))
#define rb_fd_ptr(f) (f)
#define rb_fd_init(f) FD_ZERO(f)
#define rb_fd_init_copy(d, s) (*(d) = *(s))
#define rb_fd_term(f) ((void)(f))
#define rb_fd_max(f) FD_SETSIZE
#define rb_fd_select(n, rfds, wfds, efds, timeout) \
select(fd_check((n)-1) ? (n) : FD_SETSIZE, (rfds), (wfds), (efds), (timeout))
#define rb_thread_fd_select(n, rfds, wfds, efds, timeout) \
rb_thread_select(fd_check((n)-1) ? (n) : FD_SETSIZE, (rfds), (wfds), (efds), (timeout))
#endif
// This Solaris fix is adapted from eval_intern.h in Ruby 1.9.3:
// Solaris sys/select.h switches select to select_large_fdset to support larger
// file descriptors if FD_SETSIZE is larger than 1024 on 32bit environment.
// But Ruby doesn't change FD_SETSIZE because fd_set is allocated dynamically.
// So following definition is required to use select_large_fdset.
#ifdef HAVE_SELECT_LARGE_FDSET
#define select(n, r, w, e, t) select_large_fdset((n), (r), (w), (e), (t))
extern "C" {
int select_large_fdset(int, fd_set *, fd_set *, fd_set *, struct timeval *);
}
#endif
class EventableDescriptor;
class InotifyDescriptor;
struct SelectData_t;
/*************
enum Poller_t
*************/
enum Poller_t {
Poller_Default, // typically Select
Poller_Epoll,
Poller_Kqueue
};
/********************
class EventMachine_t
********************/
class EventMachine_t
{
public:
static int GetMaxTimerCount();
static void SetMaxTimerCount (int);
static int GetSimultaneousAcceptCount();
static void SetSimultaneousAcceptCount (int);
public:
EventMachine_t (EMCallback, Poller_t);
virtual ~EventMachine_t();
bool RunOnce();
void Run();
void ScheduleHalt();
bool Stopping();
void SignalLoopBreaker();
const uintptr_t InstallOneshotTimer (uint64_t);
const uintptr_t ConnectToServer (const char *, int, const char *, int);
const uintptr_t ConnectToUnixServer (const char *);
const uintptr_t CreateTcpServer (const char *, int);
const uintptr_t OpenDatagramSocket (const char *, int);
const uintptr_t CreateUnixDomainServer (const char*);
const uintptr_t AttachSD (SOCKET);
const uintptr_t OpenKeyboard();
//const char *Popen (const char*, const char*);
const uintptr_t Socketpair (char* const*);
void Add (EventableDescriptor*);
void Modify (EventableDescriptor*);
void Deregister (EventableDescriptor*);
const uintptr_t AttachFD (SOCKET, bool);
int DetachFD (EventableDescriptor*);
void ArmKqueueWriter (EventableDescriptor*);
void ArmKqueueReader (EventableDescriptor*);
void SetTimerQuantum (int);
static void SetuidString (const char*);
static int SetRlimitNofile (int);
pid_t SubprocessPid;
int SubprocessExitStatus;
int GetConnectionCount();
float GetHeartbeatInterval();
int SetHeartbeatInterval(float);
const uintptr_t WatchFile (const char*);
void UnwatchFile (int);
void UnwatchFile (const uintptr_t);
#ifdef HAVE_KQUEUE
void _HandleKqueueFileEvent (struct kevent*);
void _RegisterKqueueFileEvent(int);
#endif
const uintptr_t WatchPid (int);
void UnwatchPid (int);
void UnwatchPid (const uintptr_t);
#ifdef HAVE_KQUEUE
void _HandleKqueuePidEvent (struct kevent*);
#endif
uint64_t GetCurrentLoopTime() { return MyCurrentLoopTime; }
void QueueHeartbeat(EventableDescriptor*);
void ClearHeartbeat(uint64_t, EventableDescriptor*);
uint64_t GetRealTime();
Poller_t GetPoller() { return Poller; }
static int name2address (const char *server, int port, int socktype, struct sockaddr *addr, size_t *addr_len);
private:
void _RunTimers();
void _UpdateTime();
void _AddNewDescriptors();
void _ModifyDescriptors();
void _InitializeLoopBreaker();
void _CleanupSockets();
void _RunSelectOnce();
void _RunEpollOnce();
void _RunKqueueOnce();
void _ModifyEpollEvent (EventableDescriptor*);
void _DispatchHeartbeats();
timeval _TimeTilNextEvent();
void _CleanBadDescriptors();
public:
void _ReadLoopBreaker();
void _ReadInotifyEvents();
int NumCloseScheduled;
private:
enum {
MaxEpollDescriptors = 64*1024,
MaxEvents = 4096
};
int HeartbeatInterval;
EMCallback EventCallback;
class Timer_t: public Bindable_t {
};
std::multimap<uint64_t, Timer_t> Timers;
std::multimap<uint64_t, EventableDescriptor*> Heartbeats;
std::map<int, Bindable_t*> Files;
std::map<int, Bindable_t*> Pids;
std::vector<EventableDescriptor*> Descriptors;
std::vector<EventableDescriptor*> NewDescriptors;
std::set<EventableDescriptor*> ModifiedDescriptors;
SOCKET LoopBreakerReader;
SOCKET LoopBreakerWriter;
#ifdef OS_WIN32
struct sockaddr_in LoopBreakerTarget;
#endif
timeval Quantum;
uint64_t MyCurrentLoopTime;
#ifdef OS_WIN32
unsigned TickCountTickover;
unsigned LastTickCount;
#endif
#ifdef OS_DARWIN
mach_timebase_info_data_t mach_timebase;
#endif
private:
bool bTerminateSignalReceived;
SelectData_t *SelectData;
Poller_t Poller;
int epfd; // Epoll file-descriptor
#ifdef HAVE_EPOLL
struct epoll_event epoll_events [MaxEvents];
#endif
int kqfd; // Kqueue file-descriptor
#ifdef HAVE_KQUEUE
struct kevent Karray [MaxEvents];
#endif
#ifdef HAVE_INOTIFY
InotifyDescriptor *inotify; // pollable descriptor for our inotify instance
#endif
};
/*******************
struct SelectData_t
*******************/
struct SelectData_t
{
SelectData_t();
~SelectData_t();
int _Select();
void _Clear();
SOCKET maxsocket;
rb_fdset_t fdreads;
rb_fdset_t fdwrites;
rb_fdset_t fderrors;
timeval tv;
int nSockets;
};
#endif // __EventMachine__H_
@@ -0,0 +1,143 @@
/*****************************************************************************
$Id$
File: eventmachine.h
Date: 15Apr06
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
Gmail: blackhedd
This program is free software; you can redistribute it and/or modify
it under the terms of either: 1) 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; or 2) Ruby's License.
See the file COPYING for complete licensing information.
*****************************************************************************/
#ifndef __EVMA_EventMachine__H_
#define __EVMA_EventMachine__H_
#if __cplusplus
extern "C" {
#endif
enum { // Event names
EM_TIMER_FIRED = 100,
EM_CONNECTION_READ = 101,
EM_CONNECTION_UNBOUND = 102,
EM_CONNECTION_ACCEPTED = 103,
EM_CONNECTION_COMPLETED = 104,
EM_LOOPBREAK_SIGNAL = 105,
EM_CONNECTION_NOTIFY_READABLE = 106,
EM_CONNECTION_NOTIFY_WRITABLE = 107,
EM_SSL_HANDSHAKE_COMPLETED = 108,
EM_SSL_VERIFY = 109,
EM_PROXY_TARGET_UNBOUND = 110,
EM_PROXY_COMPLETED = 111
};
enum { // SSL/TLS Protocols
EM_PROTO_SSLv2 = 2,
EM_PROTO_SSLv3 = 4,
EM_PROTO_TLSv1 = 8,
EM_PROTO_TLSv1_1 = 16,
EM_PROTO_TLSv1_2 = 32
};
void evma_initialize_library (EMCallback);
bool evma_run_machine_once();
void evma_run_machine();
void evma_release_library();
const uintptr_t evma_install_oneshot_timer (uint64_t milliseconds);
const uintptr_t evma_connect_to_server (const char *bind_addr, int bind_port, const char *server, int port);
const uintptr_t evma_connect_to_unix_server (const char *server);
const uintptr_t evma_attach_fd (int file_descriptor, int watch_mode);
int evma_detach_fd (const uintptr_t binding);
int evma_get_file_descriptor (const uintptr_t binding);
int evma_is_notify_readable (const uintptr_t binding);
void evma_set_notify_readable (const uintptr_t binding, int mode);
int evma_is_notify_writable (const uintptr_t binding);
void evma_set_notify_writable (const uintptr_t binding, int mode);
int evma_pause(const uintptr_t binding);
int evma_is_paused(const uintptr_t binding);
int evma_resume(const uintptr_t binding);
int evma_num_close_scheduled();
void evma_stop_tcp_server (const uintptr_t binding);
const uintptr_t evma_create_tcp_server (const char *address, int port);
const uintptr_t evma_create_unix_domain_server (const char *filename);
const uintptr_t evma_attach_sd (int sd);
const uintptr_t evma_open_datagram_socket (const char *server, int port);
const uintptr_t evma_open_keyboard();
void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filenane, int verify_peer, int fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int protocols);
void evma_start_tls (const uintptr_t binding);
#ifdef WITH_SSL
X509 *evma_get_peer_cert (const uintptr_t binding);
int evma_get_cipher_bits (const uintptr_t binding);
const char *evma_get_cipher_name (const uintptr_t binding);
const char *evma_get_cipher_protocol (const uintptr_t binding);
const char *evma_get_sni_hostname (const uintptr_t binding);
void evma_accept_ssl_peer (const uintptr_t binding);
#endif
int evma_get_peername (const uintptr_t binding, struct sockaddr*, socklen_t*);
int evma_get_sockname (const uintptr_t binding, struct sockaddr*, socklen_t*);
int evma_get_subprocess_pid (const uintptr_t binding, pid_t*);
int evma_get_subprocess_status (const uintptr_t binding, int*);
int evma_get_connection_count();
int evma_send_data_to_connection (const uintptr_t binding, const char *data, int data_length);
int evma_send_datagram (const uintptr_t binding, const char *data, int data_length, const char *address, int port);
float evma_get_comm_inactivity_timeout (const uintptr_t binding);
int evma_set_comm_inactivity_timeout (const uintptr_t binding, float value);
float evma_get_pending_connect_timeout (const uintptr_t binding);
int evma_set_pending_connect_timeout (const uintptr_t binding, float value);
int evma_get_outbound_data_size (const uintptr_t binding);
uint64_t evma_get_last_activity_time (const uintptr_t binding);
int evma_send_file_data_to_connection (const uintptr_t binding, const char *filename);
void evma_close_connection (const uintptr_t binding, int after_writing);
int evma_report_connection_error_status (const uintptr_t binding);
void evma_signal_loopbreak();
void evma_set_timer_quantum (int);
int evma_get_max_timer_count();
void evma_set_max_timer_count (int);
int evma_get_simultaneous_accept_count();
void evma_set_simultaneous_accept_count (int);
void evma_setuid_string (const char *username);
void evma_stop_machine();
bool evma_stopping();
float evma_get_heartbeat_interval();
int evma_set_heartbeat_interval(float);
const uintptr_t evma_popen (char * const*cmd_strings);
const uintptr_t evma_watch_filename (const char *fname);
void evma_unwatch_filename (const uintptr_t binding);
const uintptr_t evma_watch_pid (int);
void evma_unwatch_pid (const uintptr_t binding);
void evma_start_proxy(const uintptr_t from, const uintptr_t to, const unsigned long bufsize, const unsigned long length);
void evma_stop_proxy(const uintptr_t from);
unsigned long evma_proxied_bytes(const uintptr_t from);
int evma_set_rlimit_nofile (int n_files);
void evma_set_epoll (int use);
void evma_set_kqueue (int use);
uint64_t evma_get_current_loop_time();
#if __cplusplus
}
#endif
#endif // __EventMachine__H_
+270
View File
@@ -0,0 +1,270 @@
require 'fileutils'
require 'mkmf'
# Eager check devs tools
have_devel? if respond_to?(:have_devel?)
def check_libs libs = [], fatal = false
libs.all? { |lib| have_library(lib) || (abort("could not find library: #{lib}") if fatal) }
end
def check_heads heads = [], fatal = false
heads.all? { |head| have_header(head) || (abort("could not find header: #{head}") if fatal)}
end
def add_define(name)
$defs.push("-D#{name}")
end
##
# OpenSSL:
# override append_library, so it actually appends (instead of prepending)
# this fixes issues with linking ssl, since libcrypto depends on symbols in libssl
def append_library(libs, lib)
libs + " " + format(LIBARG, lib)
end
SSL_HEADS = %w(openssl/ssl.h openssl/err.h)
SSL_LIBS = %w(crypto ssl)
# OpenSSL 1.1.0 and above for Windows use the Unix library names
# OpenSSL 0.9.8 and 1.0.x for Windows use the *eay32 library names
SSL_LIBS_WIN = RUBY_PLATFORM =~ /mswin|mingw|bccwin/ ? %w(ssleay32 libeay32) : []
def dir_config_wrapper(pretty_name, name, idefault=nil, ldefault=nil)
inc, lib = dir_config(name, idefault, ldefault)
if inc && lib
# TODO: Remove when 2.0.0 is the minimum supported version
# Ruby versions not incorporating the mkmf fix at
# https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/39717
# do not properly search for lib directories, and must be corrected
unless lib && lib[-3, 3] == 'lib'
@libdir_basename = 'lib'
inc, lib = dir_config(name, idefault, ldefault)
end
unless idefault && ldefault
abort "-----\nCannot find #{pretty_name} include path #{inc}\n-----" unless inc && inc.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) }
abort "-----\nCannot find #{pretty_name} library path #{lib}\n-----" unless lib && lib.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) }
warn "-----\nUsing #{pretty_name} in path #{File.dirname inc}\n-----"
end
true
end
end
def dir_config_search(pretty_name, name, paths, &b)
paths.each do |p|
if dir_config_wrapper('OpenSSL', 'ssl', p + '/include', p + '/lib') && yield
warn "-----\nFound #{pretty_name} in path #{p}\n-----"
return true
end
end
false
end
def pkg_config_wrapper(pretty_name, name)
cflags, ldflags, libs = pkg_config(name)
unless [cflags, ldflags, libs].any?(&:nil?) || [cflags, ldflags, libs].any?(&:empty?)
warn "-----\nUsing #{pretty_name} from pkg-config #{cflags} && #{ldflags} && #{libs}\n-----"
true
end
end
if ENV['CROSS_COMPILING']
openssl_version = ENV.fetch("OPENSSL_VERSION", "1.0.2e")
openssl_dir = File.expand_path("~/.rake-compiler/builds/openssl-#{openssl_version}/")
if File.exist?(openssl_dir)
FileUtils.mkdir_p Dir.pwd+"/openssl/"
FileUtils.cp Dir[openssl_dir+"/include/openssl/*.h"], Dir.pwd+"/openssl/", :verbose => true
FileUtils.cp Dir[openssl_dir+"/lib*.a"], Dir.pwd, :verbose => true
$INCFLAGS << " -I#{Dir.pwd}" # for the openssl headers
add_define "WITH_SSL"
else
STDERR.puts
STDERR.puts "**************************************************************************************"
STDERR.puts "**** Cross-compiled OpenSSL not found"
STDERR.puts "**** Run: hg clone http://bitbucket.org/ged/ruby-pg && cd ruby-pg && rake openssl_libs"
STDERR.puts "**************************************************************************************"
STDERR.puts
end
elsif dir_config_wrapper('OpenSSL', 'ssl')
# If the user has provided a --with-ssl-dir argument, we must respect it or fail.
add_define 'WITH_SSL' if (check_libs(SSL_LIBS) || check_libs(SSL_LIBS_WIN)) && check_heads(SSL_HEADS)
elsif pkg_config_wrapper('OpenSSL', 'openssl')
# If we can detect OpenSSL by pkg-config, use it as the next-best option
add_define 'WITH_SSL' if (check_libs(SSL_LIBS) || check_libs(SSL_LIBS_WIN)) && check_heads(SSL_HEADS)
elsif (check_libs(SSL_LIBS) || check_libs(SSL_LIBS_WIN)) && check_heads(SSL_HEADS)
# If we don't even need any options to find a usable OpenSSL, go with it
add_define 'WITH_SSL'
elsif dir_config_search('OpenSSL', 'ssl', ['/usr/local', '/opt/local', '/usr/local/opt/openssl']) do
(check_libs(SSL_LIBS) || check_libs(SSL_LIBS_WIN)) && check_heads(SSL_HEADS)
end
# Finally, look for OpenSSL in alternate locations including MacPorts and HomeBrew
add_define 'WITH_SSL'
end
add_define 'BUILD_FOR_RUBY'
# Ruby features:
have_var('rb_trap_immediate', ['ruby.h', 'rubysig.h'])
have_func('rb_thread_blocking_region')
have_func('rb_thread_call_without_gvl', 'ruby/thread.h')
have_func('rb_thread_fd_select')
have_type('rb_fdset_t', 'ruby/intern.h')
have_func('rb_wait_for_single_fd')
have_func('rb_enable_interrupt')
have_func('rb_time_new')
# System features:
add_define('HAVE_INOTIFY') if inotify = have_func('inotify_init', 'sys/inotify.h')
add_define('HAVE_OLD_INOTIFY') if !inotify && have_macro('__NR_inotify_init', 'sys/syscall.h')
have_func('writev', 'sys/uio.h')
have_func('pipe2', 'unistd.h')
have_func('accept4', 'sys/socket.h')
have_const('SOCK_CLOEXEC', 'sys/socket.h')
# Minor platform details between *nix and Windows:
if RUBY_PLATFORM =~ /(mswin|mingw|bccwin)/
GNU_CHAIN = ENV['CROSS_COMPILING'] || $1 == 'mingw'
OS_WIN32 = true
add_define "OS_WIN32"
else
GNU_CHAIN = true
OS_UNIX = true
add_define 'OS_UNIX'
add_define "HAVE_KQUEUE" if have_header("sys/event.h") && have_header("sys/queue.h")
end
# Adjust number of file descriptors (FD) on Windows
if RbConfig::CONFIG["host_os"] =~ /mingw/
found = RbConfig::CONFIG.values_at("CFLAGS", "CPPFLAGS").
any? { |v| v.include?("FD_SETSIZE") }
add_define "FD_SETSIZE=32767" unless found
end
# Main platform invariances:
case RUBY_PLATFORM
when /mswin32/, /mingw32/, /bccwin32/
check_heads(%w[windows.h winsock.h], true)
check_libs(%w[kernel32 rpcrt4 gdi32], true)
if GNU_CHAIN
CONFIG['LDSHAREDXX'] = "$(CXX) -shared -static-libgcc -static-libstdc++"
else
$defs.push "-EHs"
$defs.push "-GR"
end
# Newer versions of Ruby already define _WIN32_WINNT, which is needed
# to get access to newer POSIX networking functions (e.g. getaddrinfo)
add_define '_WIN32_WINNT=0x0501' unless have_func('getaddrinfo')
when /solaris/
add_define 'OS_SOLARIS8'
check_libs(%w[nsl socket], true)
# If Ruby was compiled for 32-bits, then select() can only handle 1024 fds
# There is an alternate function, select_large_fdset, that supports more.
have_func('select_large_fdset', 'sys/select.h')
if CONFIG['CC'] == 'cc' && (
`cc -flags 2>&1` =~ /Sun/ || # detect SUNWspro compiler
`cc -V 2>&1` =~ /Sun/ # detect Solaris Studio compiler
)
# SUN CHAIN
add_define 'CC_SUNWspro'
$preload = ["\nCXX = CC"] # hack a CXX= line into the makefile
$CFLAGS = CONFIG['CFLAGS'] = "-KPIC"
CONFIG['CCDLFLAGS'] = "-KPIC"
CONFIG['LDSHARED'] = "$(CXX) -G -KPIC -lCstd"
CONFIG['LDSHAREDXX'] = "$(CXX) -G -KPIC -lCstd"
else
# GNU CHAIN
# on Unix we need a g++ link, not gcc.
CONFIG['LDSHARED'] = "$(CXX) -shared"
end
when /openbsd/
# OpenBSD branch contributed by Guillaume Sellier.
# on Unix we need a g++ link, not gcc. On OpenBSD, linking against libstdc++ have to be explicitly done for shared libs
CONFIG['LDSHARED'] = "$(CXX) -shared -lstdc++ -fPIC"
CONFIG['LDSHAREDXX'] = "$(CXX) -shared -lstdc++ -fPIC"
when /darwin/
add_define 'OS_DARWIN'
# on Unix we need a g++ link, not gcc.
# Ff line contributed by Daniel Harple.
CONFIG['LDSHARED'] = "$(CXX) " + CONFIG['LDSHARED'].split[1..-1].join(' ')
when /linux/
add_define 'HAVE_EPOLL' if have_func('epoll_create', 'sys/epoll.h')
# on Unix we need a g++ link, not gcc.
CONFIG['LDSHARED'] = "$(CXX) -shared"
when /aix/
CONFIG['LDSHARED'] = "$(CXX) -Wl,-bstatic -Wl,-bdynamic -Wl,-G -Wl,-brtl"
when /cygwin/
# For rubies built with Cygwin, CXX may be set to CC, which is just
# a wrapper for gcc.
# This will compile, but it will not link to the C++ std library.
# Explicitly set CXX to use g++.
CONFIG['CXX'] = "g++"
# on Unix we need a g++ link, not gcc.
CONFIG['LDSHARED'] = "$(CXX) -shared"
else
# on Unix we need a g++ link, not gcc.
CONFIG['LDSHARED'] = "$(CXX) -shared"
end
# Platform-specific time functions
if have_func('clock_gettime')
# clock_gettime is POSIX, but the monotonic clocks are not
have_const('CLOCK_MONOTONIC_RAW', 'time.h') # Linux
have_const('CLOCK_MONOTONIC', 'time.h') # Linux, Solaris, BSDs
else
have_func('gethrtime') # Older Solaris and HP-UX
end
# Hack so that try_link will test with a C++ compiler instead of a C compiler
TRY_LINK.sub!('$(CC)', '$(CXX)')
# This is our wishlist. We use whichever flags work on the host.
# In the future, add -Werror to make sure all warnings are resolved.
# deprecated-declarations are used in OS X OpenSSL
# ignored-qualifiers are used by the Bindings (would-be void *)
# unused-result because GCC 4.6 no longer silences (void) ignore_this(function)
# address because on Windows, rb_fd_select checks if &fds is non-NULL, which it cannot be
%w(
-Wall
-Wextra
-Wno-deprecated-declarations
-Wno-ignored-qualifiers
-Wno-unused-result
-Wno-address
).select do |flag|
try_link('int main() {return 0;}', flag)
end.each do |flag|
CONFIG['CXXFLAGS'] << ' ' << flag
end
puts "CXXFLAGS=#{CONFIG['CXXFLAGS']}"
# Solaris C++ compiler doesn't have make_pair()
add_define 'HAVE_MAKE_PAIR' if try_link(<<SRC, '-lstdc++')
#include <utility>
using namespace std;
int main(){ pair<const int,int> tuple = make_pair(1,2); }
SRC
TRY_LINK.sub!('$(CXX)', '$(CC)')
create_makefile "rubyeventmachine"
@@ -0,0 +1,269 @@
SHELL = /bin/sh
# V=0 quiet, V=1 verbose. other values don't work.
V = 0
V0 = $(V:0=)
Q1 = $(V:1=)
Q = $(Q1:0=@)
ECHO1 = $(V:1=@ :)
ECHO = $(ECHO1:0=@ echo)
NULLCMD = :
#### Start of system configuration section. ####
srcdir = .
topdir = /usr/include/ruby-3.2.0
hdrdir = $(topdir)
arch_hdrdir = /usr/include/x86_64-linux-gnu/ruby-3.2.0
PATH_SEPARATOR = :
VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
prefix = $(DESTDIR)/usr
rubysitearchprefix = $(sitearchlibdir)/$(RUBY_BASE_NAME)
rubyarchprefix = $(archlibdir)/$(RUBY_BASE_NAME)
rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
exec_prefix = $(prefix)
vendorarchhdrdir = $(sitearchincludedir)/$(RUBY_VERSION_NAME)/vendor_ruby
sitearchhdrdir = $(sitearchincludedir)/$(RUBY_VERSION_NAME)/site_ruby
rubyarchhdrdir = $(archincludedir)/$(RUBY_VERSION_NAME)
vendorhdrdir = $(rubyhdrdir)/vendor_ruby
sitehdrdir = $(rubyhdrdir)/site_ruby
rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME)
vendorarchdir = $(rubysitearchprefix)/vendor_ruby/$(ruby_version)
vendorlibdir = $(vendordir)/$(ruby_version)
vendordir = $(rubylibprefix)/vendor_ruby
sitearchdir = $(DESTDIR)/usr/local/lib/x86_64-linux-gnu/site_ruby
sitelibdir = $(sitedir)/$(ruby_version)
sitedir = $(DESTDIR)/usr/local/lib/site_ruby
rubyarchdir = $(rubyarchprefix)/$(ruby_version)
rubylibdir = $(rubylibprefix)/$(ruby_version)
sitearchincludedir = $(includedir)/$(sitearch)
archincludedir = $(includedir)/$(arch)
sitearchlibdir = $(libdir)/$(sitearch)
archlibdir = $(libdir)/$(arch)
ridir = $(datarootdir)/$(RI_BASE_NAME)
mandir = $(datarootdir)/man
localedir = $(datarootdir)/locale
libdir = $(exec_prefix)/lib
psdir = $(docdir)
pdfdir = $(docdir)
dvidir = $(docdir)
htmldir = $(docdir)
infodir = $(datarootdir)/info
docdir = $(datarootdir)/doc/$(PACKAGE)
oldincludedir = $(DESTDIR)/usr/include
includedir = $(prefix)/include
runstatedir = $(DESTDIR)/var/run
localstatedir = $(DESTDIR)/var
sharedstatedir = $(prefix)/com
sysconfdir = $(DESTDIR)/etc
datadir = $(datarootdir)
datarootdir = $(prefix)/share
libexecdir = $(exec_prefix)/libexec
sbindir = $(exec_prefix)/sbin
bindir = $(exec_prefix)/bin
archdir = $(rubyarchdir)
CC_WRAPPER =
CC = x86_64-linux-gnu-gcc
CXX = x86_64-linux-gnu-g++
LIBRUBY = $(LIBRUBY_SO)
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static $(MAINLIBS)
empty =
OUTFLAG = -o $(empty)
COUTFLAG = -o $(empty)
CSRCFLAG = $(empty)
RUBY_EXTCONF_H =
cflags = $(optflags) $(debugflags) $(warnflags)
cxxflags =
optflags = -O3 -fno-fast-math
debugflags = -ggdb3
warnflags = -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wmisleading-indentation -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wundef
cppflags =
CCDLFLAGS = -fPIC
CFLAGS = $(CCDLFLAGS) -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=BUILDDIR=. -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection -fdebug-prefix-map=BUILDDIR=/usr/src/ruby3.2-3.2.3-1ubuntu0.24.04.8 -fPIC $(ARCH_FLAG)
INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
DEFS =
CPPFLAGS = -DBUILD_FOR_RUBY -DOS_UNIX -Wdate-time -D_FORTIFY_SOURCE=3 $(DEFS) $(cppflags)
CXXFLAGS = $(CCDLFLAGS) -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=BUILDDIR=. -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection -fdebug-prefix-map=BUILDDIR=/usr/src/ruby3.2-3.2.3-1ubuntu0.24.04.8 $(ARCH_FLAG)
ldflags = -L. -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed
dldflags = -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now
ARCH_FLAG =
DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
LDSHARED = $(CXX) -shared
LDSHAREDXX = $(CXX) -shared
AR = x86_64-linux-gnu-gcc-ar
EXEEXT =
RUBY_INSTALL_NAME = $(RUBY_BASE_NAME)3.2
RUBY_SO_NAME = ruby-3.2
RUBYW_INSTALL_NAME =
RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version)
RUBYW_BASE_NAME = rubyw
RUBY_BASE_NAME = ruby
arch = x86_64-linux-gnu
sitearch = $(arch)
ruby_version = 3.2.0
ruby = $(bindir)/$(RUBY_BASE_NAME)3.2
RUBY = $(ruby)
BUILTRUBY = $(bindir)/$(RUBY_BASE_NAME)3.2
ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h
RM = rm -f
RM_RF = rm -fr
RMDIRS = rmdir --ignore-fail-on-non-empty -p
MAKEDIRS = /bin/mkdir -p
INSTALL = /usr/bin/install -c
INSTALL_PROG = $(INSTALL) -m 0755
INSTALL_DATA = $(INSTALL) -m 644
COPY = cp
TOUCH = exit >
#### End of system configuration section. ####
preload =
libpath = . $(archlibdir)
LIBPATH = -L. -L$(archlibdir)
DEFFILE =
CLEANFILES = mkmf.log
DISTCLEANFILES =
DISTCLEANDIRS =
extout =
extout_prefix =
target_prefix =
LOCAL_LIBS =
LIBS = $(LIBRUBYARG_SHARED) -lm -lpthread -lc
ORIG_SRCS = mapper.cpp rubymain.cpp
SRCS = $(ORIG_SRCS)
OBJS = mapper.o rubymain.o
HDRS = $(srcdir)/mapper.h
LOCAL_HDRS =
TARGET = fastfilereaderext
TARGET_NAME = fastfilereaderext
TARGET_ENTRY = Init_$(TARGET_NAME)
DLLIB = $(TARGET).so
EXTSTATIC =
STATIC_LIB =
TIMESTAMP_DIR = .
BINDIR = $(bindir)
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
RUBYLIBDIR = $(sitelibdir)$(target_prefix)
RUBYARCHDIR = $(sitearchdir)$(target_prefix)
HDRDIR = $(sitehdrdir)$(target_prefix)
ARCHHDRDIR = $(sitearchhdrdir)$(target_prefix)
TARGET_SO_DIR =
TARGET_SO = $(TARGET_SO_DIR)$(DLLIB)
CLEANLIBS = $(TARGET_SO) false
CLEANOBJS = $(OBJS) *.bak
TARGET_SO_DIR_TIMESTAMP = $(TIMESTAMP_DIR)/.sitearchdir.time
all: $(DLLIB)
static: $(STATIC_LIB)
.PHONY: all install static install-so install-rb
.PHONY: clean clean-so clean-static clean-rb
clean-static::
clean-rb-default::
clean-rb::
clean-so::
clean: clean-so clean-static clean-rb-default clean-rb
-$(Q)$(RM_RF) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time
distclean-rb-default::
distclean-rb::
distclean-so::
distclean-static::
distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
-$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
-$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
-$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true
realclean: distclean
install: install-so install-rb
install-so: $(DLLIB) $(TARGET_SO_DIR_TIMESTAMP)
$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
clean-static::
-$(Q)$(RM) $(STATIC_LIB)
install-rb: pre-install-rb do-install-rb install-rb-default
install-rb-default: pre-install-rb-default do-install-rb-default
pre-install-rb: Makefile
pre-install-rb-default: Makefile
do-install-rb:
do-install-rb-default:
pre-install-rb-default:
@$(NULLCMD)
$(TARGET_SO_DIR_TIMESTAMP):
$(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR)
$(Q) $(TOUCH) $@
site-install: site-install-so site-install-rb
site-install-so: install-so
site-install-rb: install-rb
.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S
.cc.o:
$(ECHO) compiling $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.cc.S:
$(ECHO) translating $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
.mm.o:
$(ECHO) compiling $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.mm.S:
$(ECHO) translating $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
.cxx.o:
$(ECHO) compiling $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.cxx.S:
$(ECHO) translating $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
.cpp.o:
$(ECHO) compiling $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.cpp.S:
$(ECHO) translating $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
.c.o:
$(ECHO) compiling $(<)
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.c.S:
$(ECHO) translating $(<)
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
.m.o:
$(ECHO) compiling $(<)
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.m.S:
$(ECHO) translating $(<)
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
$(TARGET_SO): $(OBJS) Makefile
$(ECHO) linking shared-object $(DLLIB)
-$(Q)$(RM) $(@)
$(Q) $(LDSHAREDXX) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
$(OBJS): $(HDRS) $(ruby_headers)
@@ -0,0 +1,109 @@
require 'mkmf'
def check_libs libs = [], fatal = false
libs.all? { |lib| have_library(lib) || (abort("could not find library: #{lib}") if fatal) }
end
def check_heads heads = [], fatal = false
heads.all? { |head| have_header(head) || (abort("could not find header: #{head}") if fatal)}
end
def add_define(name)
$defs.push("-D#{name}")
end
# Eager check devs tools
have_devel? if respond_to?(:have_devel?)
add_define 'BUILD_FOR_RUBY'
# Minor platform details between *nix and Windows:
if RUBY_PLATFORM =~ /(mswin|mingw|bccwin)/
GNU_CHAIN = ENV['CROSS_COMPILING'] || $1 == 'mingw'
OS_WIN32 = true
add_define "OS_WIN32"
else
GNU_CHAIN = true
OS_UNIX = true
add_define 'OS_UNIX'
end
# Adjust number of file descriptors (FD) on Windows
if RbConfig::CONFIG["host_os"] =~ /mingw/
found = RbConfig::CONFIG.values_at("CFLAGS", "CPPFLAGS").
any? { |v| v.include?("FD_SETSIZE") }
add_define "FD_SETSIZE=32767" unless found
end
# Main platform invariances:
case RUBY_PLATFORM
when /mswin32/, /mingw32/, /bccwin32/
check_heads(%w[windows.h winsock.h], true)
check_libs(%w[kernel32 rpcrt4 gdi32], true)
if GNU_CHAIN
CONFIG['LDSHAREDXX'] = "$(CXX) -shared -static-libgcc -static-libstdc++"
else
$defs.push "-EHs"
$defs.push "-GR"
end
when /solaris/
add_define 'OS_SOLARIS8'
check_libs(%w[nsl socket], true)
if CONFIG['CC'] == 'cc' && (
`cc -flags 2>&1` =~ /Sun/ || # detect SUNWspro compiler
`cc -V 2>&1` =~ /Sun/ # detect Solaris Studio compiler
)
# SUN CHAIN
add_define 'CC_SUNWspro'
$preload = ["\nCXX = CC"] # hack a CXX= line into the makefile
$CFLAGS = CONFIG['CFLAGS'] = "-KPIC"
CONFIG['CCDLFLAGS'] = "-KPIC"
CONFIG['LDSHARED'] = "$(CXX) -G -KPIC -lCstd"
CONFIG['LDSHAREDXX'] = "$(CXX) -G -KPIC -lCstd"
else
# GNU CHAIN
# on Unix we need a g++ link, not gcc.
CONFIG['LDSHARED'] = "$(CXX) -shared"
end
when /openbsd/
# OpenBSD branch contributed by Guillaume Sellier.
# on Unix we need a g++ link, not gcc. On OpenBSD, linking against libstdc++ have to be explicitly done for shared libs
CONFIG['LDSHARED'] = "$(CXX) -shared -lstdc++ -fPIC"
CONFIG['LDSHAREDXX'] = "$(CXX) -shared -lstdc++ -fPIC"
when /darwin/
# on Unix we need a g++ link, not gcc.
# Ff line contributed by Daniel Harple.
CONFIG['LDSHARED'] = "$(CXX) " + CONFIG['LDSHARED'].split[1..-1].join(' ')
when /linux/
# on Unix we need a g++ link, not gcc.
CONFIG['LDSHARED'] = "$(CXX) -shared"
when /aix/
CONFIG['LDSHARED'] = "$(CXX) -Wl,-bstatic -Wl,-bdynamic -Wl,-G -Wl,-brtl"
when /cygwin/
# For rubies built with Cygwin, CXX may be set to CC, which is just
# a wrapper for gcc.
# This will compile, but it will not link to the C++ std library.
# Explicitly set CXX to use g++.
CONFIG['CXX'] = "g++"
# on Unix we need a g++ link, not gcc.
CONFIG['LDSHARED'] = "$(CXX) -shared"
else
# on Unix we need a g++ link, not gcc.
CONFIG['LDSHARED'] = "$(CXX) -shared"
end
create_makefile "fastfilereaderext"
@@ -0,0 +1,214 @@
/*****************************************************************************
$Id: mapper.cpp 4527 2007-07-04 10:21:34Z francis $
File: mapper.cpp
Date: 02Jul07
Copyright (C) 2007 by Francis Cianfrocca. All Rights Reserved.
Gmail: garbagecat10
This program is free software; you can redistribute it and/or modify
it under the terms of either: 1) 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; or 2) Ruby's License.
See the file COPYING for complete licensing information.
*****************************************************************************/
//////////////////////////////////////////////////////////////////////
// UNIX implementation
//////////////////////////////////////////////////////////////////////
#ifdef OS_UNIX
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <iostream>
#include <string>
#include <cstring>
#include <stdexcept>
#include "mapper.h"
/******************
Mapper_t::Mapper_t
******************/
Mapper_t::Mapper_t (const std::string &filename)
{
/* We ASSUME we can open the file.
* (More precisely, we assume someone else checked before we got here.)
*/
Fd = open (filename.c_str(), O_RDONLY);
if (Fd < 0)
throw std::runtime_error (strerror (errno));
struct stat st;
if (fstat (Fd, &st))
throw std::runtime_error (strerror (errno));
FileSize = st.st_size;
#ifdef OS_WIN32
MapPoint = (char*) mmap (0, FileSize, PROT_READ, MAP_SHARED, Fd, 0);
#else
MapPoint = (const char*) mmap (0, FileSize, PROT_READ, MAP_SHARED, Fd, 0);
#endif
if (MapPoint == MAP_FAILED)
throw std::runtime_error (strerror (errno));
}
/*******************
Mapper_t::~Mapper_t
*******************/
Mapper_t::~Mapper_t()
{
Close();
}
/***************
Mapper_t::Close
***************/
void Mapper_t::Close()
{
// Can be called multiple times.
// Calls to GetChunk are invalid after a call to Close.
if (MapPoint) {
#ifdef CC_SUNWspro
// TODO: The void * cast works fine on Solaris 11, but
// I don't know at what point that changed from older Solaris.
munmap ((char*)MapPoint, FileSize);
#else
munmap ((void*)MapPoint, FileSize);
#endif
MapPoint = NULL;
}
if (Fd >= 0) {
close (Fd);
Fd = -1;
}
}
/******************
Mapper_t::GetChunk
******************/
const char *Mapper_t::GetChunk (unsigned start)
{
return MapPoint + start;
}
#endif // OS_UNIX
//////////////////////////////////////////////////////////////////////
// WINDOWS implementation
//////////////////////////////////////////////////////////////////////
#ifdef OS_WIN32
#include <windows.h>
#include <iostream>
#include <string>
#include <stdexcept>
#include "mapper.h"
/******************
Mapper_t::Mapper_t
******************/
Mapper_t::Mapper_t (const std::string &filename)
{
/* We ASSUME we can open the file.
* (More precisely, we assume someone else checked before we got here.)
*/
hFile = INVALID_HANDLE_VALUE;
hMapping = NULL;
MapPoint = NULL;
FileSize = 0;
hFile = CreateFile (filename.c_str(), GENERIC_READ|GENERIC_WRITE, FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
throw std::runtime_error ("File not found");
BY_HANDLE_FILE_INFORMATION i;
if (GetFileInformationByHandle (hFile, &i))
FileSize = i.nFileSizeLow;
hMapping = CreateFileMapping (hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
if (!hMapping)
throw std::runtime_error ("File not mapped");
#ifdef OS_WIN32
MapPoint = (char*) MapViewOfFile (hMapping, FILE_MAP_WRITE, 0, 0, 0);
#else
MapPoint = (const char*) MapViewOfFile (hMapping, FILE_MAP_WRITE, 0, 0, 0);
#endif
if (!MapPoint)
throw std::runtime_error ("Mappoint not read");
}
/*******************
Mapper_t::~Mapper_t
*******************/
Mapper_t::~Mapper_t()
{
Close();
}
/***************
Mapper_t::Close
***************/
void Mapper_t::Close()
{
// Can be called multiple times.
// Calls to GetChunk are invalid after a call to Close.
if (MapPoint) {
UnmapViewOfFile (MapPoint);
MapPoint = NULL;
}
if (hMapping != NULL) {
CloseHandle (hMapping);
hMapping = NULL;
}
if (hFile != INVALID_HANDLE_VALUE) {
CloseHandle (hFile);
hFile = INVALID_HANDLE_VALUE;
}
}
/******************
Mapper_t::GetChunk
******************/
const char *Mapper_t::GetChunk (unsigned start)
{
return MapPoint + start;
}
#endif // OS_WINDOWS
@@ -0,0 +1,59 @@
/*****************************************************************************
$Id: mapper.h 4529 2007-07-04 11:32:22Z francis $
File: mapper.h
Date: 02Jul07
Copyright (C) 2007 by Francis Cianfrocca. All Rights Reserved.
Gmail: garbagecat10
This program is free software; you can redistribute it and/or modify
it under the terms of either: 1) 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; or 2) Ruby's License.
See the file COPYING for complete licensing information.
*****************************************************************************/
#ifndef __Mapper__H_
#define __Mapper__H_
/**************
class Mapper_t
**************/
class Mapper_t
{
public:
Mapper_t (const std::string&);
virtual ~Mapper_t();
const char *GetChunk (unsigned);
void Close();
size_t GetFileSize() {return FileSize;}
private:
size_t FileSize;
#ifdef OS_UNIX
private:
int Fd;
const char *MapPoint;
#endif // OS_UNIX
#ifdef OS_WIN32
private:
HANDLE hFile;
HANDLE hMapping;
char *MapPoint;
#endif // OS_WIN32
};
#endif // __Mapper__H_
@@ -0,0 +1,126 @@
/*****************************************************************************
$Id: rubymain.cpp 4529 2007-07-04 11:32:22Z francis $
File: rubymain.cpp
Date: 02Jul07
Copyright (C) 2007 by Francis Cianfrocca. All Rights Reserved.
Gmail: garbagecat10
This program is free software; you can redistribute it and/or modify
it under the terms of either: 1) 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; or 2) Ruby's License.
See the file COPYING for complete licensing information.
*****************************************************************************/
#include <iostream>
#include <stdexcept>
#include <ruby.h>
#include "mapper.h"
static VALUE EmModule;
static VALUE FastFileReader;
static VALUE Mapper;
/*********
mapper_dt
*********/
static void mapper_dt (void *ptr)
{
if (ptr)
delete (Mapper_t*) ptr;
}
/**********
mapper_new
**********/
static VALUE mapper_new (VALUE self, VALUE filename)
{
Mapper_t *m = new Mapper_t (StringValueCStr (filename));
if (!m)
rb_raise (rb_eStandardError, "No Mapper Object");
VALUE v = Data_Wrap_Struct (Mapper, 0, mapper_dt, (void*)m);
return v;
}
/****************
mapper_get_chunk
****************/
static VALUE mapper_get_chunk (VALUE self, VALUE start, VALUE length)
{
Mapper_t *m = NULL;
Data_Get_Struct (self, Mapper_t, m);
if (!m)
rb_raise (rb_eStandardError, "No Mapper Object");
// TODO, what if some moron sends us a negative start value?
unsigned _start = NUM2INT (start);
unsigned _length = NUM2INT (length);
if ((_start + _length) > m->GetFileSize())
rb_raise (rb_eStandardError, "Mapper Range Error");
const char *chunk = m->GetChunk (_start);
if (!chunk)
rb_raise (rb_eStandardError, "No Mapper Chunk");
return rb_str_new (chunk, _length);
}
/************
mapper_close
************/
static VALUE mapper_close (VALUE self)
{
Mapper_t *m = NULL;
Data_Get_Struct (self, Mapper_t, m);
if (!m)
rb_raise (rb_eStandardError, "No Mapper Object");
m->Close();
return Qnil;
}
/***********
mapper_size
***********/
static VALUE mapper_size (VALUE self)
{
Mapper_t *m = NULL;
Data_Get_Struct (self, Mapper_t, m);
if (!m)
rb_raise (rb_eStandardError, "No Mapper Object");
return INT2NUM (m->GetFileSize());
}
/**********************
Init_fastfilereaderext
**********************/
extern "C" void Init_fastfilereaderext()
{
EmModule = rb_define_module ("EventMachine");
FastFileReader = rb_define_class_under (EmModule, "FastFileReader", rb_cObject);
Mapper = rb_define_class_under (FastFileReader, "Mapper", rb_cObject);
rb_define_module_function (Mapper, "new", (VALUE(*)(...))mapper_new, 1);
rb_define_method (Mapper, "size", (VALUE(*)(...))mapper_size, 0);
rb_define_method (Mapper, "close", (VALUE(*)(...))mapper_close, 0);
rb_define_method (Mapper, "get_chunk", (VALUE(*)(...))mapper_get_chunk, 2);
}
+79
View File
@@ -0,0 +1,79 @@
/*****************************************************************************
$Id$
File: kb.cpp
Date: 24Aug07
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
Gmail: blackhedd
This program is free software; you can redistribute it and/or modify
it under the terms of either: 1) 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; or 2) Ruby's License.
See the file COPYING for complete licensing information.
*****************************************************************************/
#include "project.h"
/**************************************
KeyboardDescriptor::KeyboardDescriptor
**************************************/
KeyboardDescriptor::KeyboardDescriptor (EventMachine_t *parent_em):
EventableDescriptor (0, parent_em),
bReadAttemptedAfterClose (false)
{
#ifdef HAVE_EPOLL
EpollEvent.events = EPOLLIN;
#endif
#ifdef HAVE_KQUEUE
MyEventMachine->ArmKqueueReader (this);
#endif
}
/***************************************
KeyboardDescriptor::~KeyboardDescriptor
***************************************/
KeyboardDescriptor::~KeyboardDescriptor()
{
}
/*************************
KeyboardDescriptor::Write
*************************/
void KeyboardDescriptor::Write()
{
// Why are we here?
throw std::runtime_error ("bad code path in keyboard handler");
}
/*****************************
KeyboardDescriptor::Heartbeat
*****************************/
void KeyboardDescriptor::Heartbeat()
{
// no-op
}
/************************
KeyboardDescriptor::Read
************************/
void KeyboardDescriptor::Read()
{
char c;
(void)read (GetSocket(), &c, 1);
_GenericInboundDispatch(&c, 1);
}
+107
View File
@@ -0,0 +1,107 @@
/*****************************************************************************
$Id$
File: page.cpp
Date: 30Apr06
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
Gmail: blackhedd
This program is free software; you can redistribute it and/or modify
it under the terms of either: 1) 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; or 2) Ruby's License.
See the file COPYING for complete licensing information.
*****************************************************************************/
#include "project.h"
/******************
PageList::PageList
******************/
PageList::PageList()
{
}
/*******************
PageList::~PageList
*******************/
PageList::~PageList()
{
while (HasPages())
PopFront();
}
/***************
PageList::Front
***************/
void PageList::Front (const char **page, int *length)
{
assert (page && length);
if (HasPages()) {
Page p = Pages.front();
*page = p.Buffer;
*length = p.Size;
}
else {
*page = NULL;
*length = 0;
}
}
/******************
PageList::PopFront
******************/
void PageList::PopFront()
{
if (HasPages()) {
Page p = Pages.front();
Pages.pop_front();
if (p.Buffer)
free ((void*)p.Buffer);
}
}
/******************
PageList::HasPages
******************/
bool PageList::HasPages()
{
return (Pages.size() > 0) ? true : false;
}
/**************
PageList::Push
**************/
void PageList::Push (const char *buf, int size)
{
if (buf && (size > 0)) {
char *copy = (char*) malloc (size);
if (!copy)
throw std::runtime_error ("no memory in pagelist");
memcpy (copy, buf, size);
Pages.push_back (Page (copy, size));
}
}
+51
View File
@@ -0,0 +1,51 @@
/*****************************************************************************
$Id$
File: page.h
Date: 30Apr06
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
Gmail: blackhedd
This program is free software; you can redistribute it and/or modify
it under the terms of either: 1) 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; or 2) Ruby's License.
See the file COPYING for complete licensing information.
*****************************************************************************/
#ifndef __PageManager__H_
#define __PageManager__H_
/**************
class PageList
**************/
class PageList
{
struct Page {
Page (const char *b, size_t s): Buffer(b), Size(s) {}
const char *Buffer;
size_t Size;
};
public:
PageList();
virtual ~PageList();
void Push (const char*, int);
bool HasPages();
void Front (const char**, int*);
void PopFront();
private:
std::deque<Page> Pages;
};
#endif // __PageManager__H_
+354
View File
@@ -0,0 +1,354 @@
/*****************************************************************************
$Id$
File: pipe.cpp
Date: 30May07
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
Gmail: blackhedd
This program is free software; you can redistribute it and/or modify
it under the terms of either: 1) 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; or 2) Ruby's License.
See the file COPYING for complete licensing information.
*****************************************************************************/
#include "project.h"
#ifdef OS_UNIX
// THIS ENTIRE FILE IS ONLY COMPILED ON UNIX-LIKE SYSTEMS.
/******************************
PipeDescriptor::PipeDescriptor
******************************/
PipeDescriptor::PipeDescriptor (int fd, pid_t subpid, EventMachine_t *parent_em):
EventableDescriptor (fd, parent_em),
bReadAttemptedAfterClose (false),
OutboundDataSize (0),
SubprocessPid (subpid)
{
#ifdef HAVE_EPOLL
EpollEvent.events = EPOLLIN;
#endif
#ifdef HAVE_KQUEUE
MyEventMachine->ArmKqueueReader (this);
#endif
}
/*******************************
PipeDescriptor::~PipeDescriptor
*******************************/
PipeDescriptor::~PipeDescriptor() NO_EXCEPT_FALSE
{
// Run down any stranded outbound data.
for (size_t i=0; i < OutboundPages.size(); i++)
OutboundPages[i].Free();
/* As a virtual destructor, we come here before the base-class
* destructor that closes our file-descriptor.
* We have to make sure the subprocess goes down (if it's not
* already down) and we have to reap the zombie.
*
* This implementation is PROVISIONAL and will surely be improved.
* The intention here is that we never block, hence the highly
* undesirable sleeps. But if we can't reap the subprocess even
* after sending it SIGKILL, then something is wrong and we
* throw a fatal exception, which is also not something we should
* be doing.
*
* Eventually the right thing to do will be to have the reactor
* core respond to SIGCHLD by chaining a handler on top of the
* one Ruby may have installed, and dealing with a list of dead
* children that are pending cleanup.
*
* Since we want to have a signal processor integrated into the
* client-visible API, let's wait until that is done before cleaning
* this up.
*
* Added a very ugly hack to support passing the subprocess's exit
* status to the user. It only makes logical sense for user code to access
* the subprocess exit status in the unbind callback. But unbind is called
* back during the EventableDescriptor destructor. So by that time there's
* no way to call back this object through an object binding, because it's
* already been cleaned up. We might have added a parameter to the unbind
* callback, but that would probably break a huge amount of existing code.
* So the hack-solution is to define an instance variable in the EventMachine
* object and stick the exit status in there, where it can easily be accessed
* with an accessor visible to user code.
* User code should ONLY access the exit status from within the unbind callback.
* Otherwise there's no guarantee it'll be valid.
* This hack won't make it impossible to run multiple EventMachines in a single
* process, but it will make it impossible to reliably nest unbind calls
* within other unbind calls. (Not sure if that's even possible.)
*/
assert (MyEventMachine);
/* Another hack to make the SubprocessPid available to get_subprocess_status */
MyEventMachine->SubprocessPid = SubprocessPid;
/* 01Mar09: Updated to use a small nanosleep in a loop. When nanosleep is interrupted by SIGCHLD,
* it resumes the system call after processing the signal (resulting in unnecessary latency).
* Calling nanosleep in a loop avoids this problem.
*/
struct timespec req = {0, 50000000}; // 0.05s
int n;
// wait 0.5s for the process to die
for (n=0; n<10; n++) {
if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) != 0) return;
nanosleep (&req, NULL);
}
// send SIGTERM and wait another 1s
kill (SubprocessPid, SIGTERM);
for (n=0; n<20; n++) {
nanosleep (&req, NULL);
if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) != 0) return;
}
// send SIGKILL and wait another 5s
kill (SubprocessPid, SIGKILL);
for (n=0; n<100; n++) {
nanosleep (&req, NULL);
if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) != 0) return;
}
// still not dead, give up!
throw std::runtime_error ("unable to reap subprocess");
}
/********************
PipeDescriptor::Read
********************/
void PipeDescriptor::Read()
{
int sd = GetSocket();
if (sd == INVALID_SOCKET) {
assert (!bReadAttemptedAfterClose);
bReadAttemptedAfterClose = true;
return;
}
LastActivity = MyEventMachine->GetCurrentLoopTime();
int total_bytes_read = 0;
char readbuffer [16 * 1024];
for (int i=0; i < 10; i++) {
// Don't read just one buffer and then move on. This is faster
// if there is a lot of incoming.
// But don't read indefinitely. Give other sockets a chance to run.
// NOTICE, we're reading one less than the buffer size.
// That's so we can put a guard byte at the end of what we send
// to user code.
// Use read instead of recv, which on Linux gives a "socket operation
// on nonsocket" error.
int r = read (sd, readbuffer, sizeof(readbuffer) - 1);
//cerr << "<R:" << r << ">";
if (r > 0) {
total_bytes_read += r;
// Add a null-terminator at the the end of the buffer
// that we will send to the callback.
// DO NOT EVER CHANGE THIS. We want to explicitly allow users
// to be able to depend on this behavior, so they will have
// the option to do some things faster. Additionally it's
// a security guard against buffer overflows.
readbuffer [r] = 0;
_GenericInboundDispatch(readbuffer, r);
}
else if (r == 0) {
break;
}
else {
// Basically a would-block, meaning we've read everything there is to read.
break;
}
}
if (total_bytes_read == 0) {
// If we read no data on a socket that selected readable,
// it generally means the other end closed the connection gracefully.
ScheduleClose (false);
//bCloseNow = true;
}
}
/*********************
PipeDescriptor::Write
*********************/
void PipeDescriptor::Write()
{
int sd = GetSocket();
assert (sd != INVALID_SOCKET);
LastActivity = MyEventMachine->GetCurrentLoopTime();
char output_buffer [16 * 1024];
size_t nbytes = 0;
while ((OutboundPages.size() > 0) && (nbytes < sizeof(output_buffer))) {
OutboundPage *op = &(OutboundPages[0]);
if ((nbytes + op->Length - op->Offset) < sizeof (output_buffer)) {
memcpy (output_buffer + nbytes, op->Buffer + op->Offset, op->Length - op->Offset);
nbytes += (op->Length - op->Offset);
op->Free();
OutboundPages.pop_front();
}
else {
int len = sizeof(output_buffer) - nbytes;
memcpy (output_buffer + nbytes, op->Buffer + op->Offset, len);
op->Offset += len;
nbytes += len;
}
}
// We should never have gotten here if there were no data to write,
// so assert that as a sanity check.
// Don't bother to make sure nbytes is less than output_buffer because
// if it were we probably would have crashed already.
assert (nbytes > 0);
assert (GetSocket() != INVALID_SOCKET);
int bytes_written = write (GetSocket(), output_buffer, nbytes);
#ifdef OS_WIN32
int e = WSAGetLastError();
#else
int e = errno;
#endif
if (bytes_written > 0) {
OutboundDataSize -= bytes_written;
if ((size_t)bytes_written < nbytes) {
int len = nbytes - bytes_written;
char *buffer = (char*) malloc (len + 1);
if (!buffer)
throw std::runtime_error ("bad alloc throwing back data");
memcpy (buffer, output_buffer + bytes_written, len);
buffer [len] = 0;
OutboundPages.push_front (OutboundPage (buffer, len));
}
#ifdef HAVE_EPOLL
EpollEvent.events = EPOLLIN;
if (SelectForWrite())
EpollEvent.events |= EPOLLOUT;
assert (MyEventMachine);
MyEventMachine->Modify (this);
#endif
}
else {
#ifdef OS_UNIX
if ((e != EINPROGRESS) && (e != EWOULDBLOCK) && (e != EINTR))
#endif
#ifdef OS_WIN32
if ((e != WSAEINPROGRESS) && (e != WSAEWOULDBLOCK))
#endif
Close();
}
}
/*************************
PipeDescriptor::Heartbeat
*************************/
void PipeDescriptor::Heartbeat()
{
// If an inactivity timeout is defined, then check for it.
if (InactivityTimeout && ((MyEventMachine->GetCurrentLoopTime() - LastActivity) >= InactivityTimeout))
ScheduleClose (false);
//bCloseNow = true;
}
/*****************************
PipeDescriptor::SelectForRead
*****************************/
bool PipeDescriptor::SelectForRead()
{
/* Pipe descriptors, being local by definition, don't have
* a pending state, so this is simpler than for the
* ConnectionDescriptor object.
*/
return bPaused ? false : true;
}
/******************************
PipeDescriptor::SelectForWrite
******************************/
bool PipeDescriptor::SelectForWrite()
{
/* Pipe descriptors, being local by definition, don't have
* a pending state, so this is simpler than for the
* ConnectionDescriptor object.
*/
return (GetOutboundDataSize() > 0) && !bPaused ? true : false;
}
/********************************
PipeDescriptor::SendOutboundData
********************************/
int PipeDescriptor::SendOutboundData (const char *data, unsigned long length)
{
//if (bCloseNow || bCloseAfterWriting)
if (IsCloseScheduled())
return 0;
if (!data && (length > 0))
throw std::runtime_error ("bad outbound data");
char *buffer = (char *) malloc (length + 1);
if (!buffer)
throw std::runtime_error ("no allocation for outbound data");
memcpy (buffer, data, length);
buffer [length] = 0;
OutboundPages.push_back (OutboundPage (buffer, length));
OutboundDataSize += length;
#ifdef HAVE_EPOLL
EpollEvent.events = (EPOLLIN | EPOLLOUT);
assert (MyEventMachine);
MyEventMachine->Modify (this);
#endif
return length;
}
/********************************
PipeDescriptor::GetSubprocessPid
********************************/
bool PipeDescriptor::GetSubprocessPid (pid_t *pid)
{
bool ok = false;
if (pid && (SubprocessPid > 0)) {
*pid = SubprocessPid;
ok = true;
}
return ok;
}
#endif // OS_UNIX
+174
View File
@@ -0,0 +1,174 @@
/*****************************************************************************
$Id$
File: project.h
Date: 06Apr06
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
Gmail: blackhedd
This program is free software; you can redistribute it and/or modify
it under the terms of either: 1) 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; or 2) Ruby's License.
See the file COPYING for complete licensing information.
*****************************************************************************/
#ifndef __Project__H_
#define __Project__H_
#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <deque>
#include <string>
#include <sstream>
#include <stdexcept>
#ifdef OS_UNIX
#include <signal.h>
#include <netdb.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <pwd.h>
#include <string.h>
typedef int SOCKET;
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#ifdef OS_SOLARIS8
#include <strings.h>
#include <sys/un.h>
#ifndef AF_LOCAL
#define AF_LOCAL AF_UNIX
#endif
// INADDR_NONE is undefined on Solaris < 8. Thanks to Brett Eisenberg and Tim Pease.
#ifndef INADDR_NONE
#define INADDR_NONE ((unsigned long)-1)
#endif
#endif /* OS_SOLARIS8 */
#ifdef _AIX
#include <strings.h>
#ifndef AF_LOCAL
#define AF_LOCAL AF_UNIX
#endif
#endif /* _AIX */
#ifdef OS_DARWIN
#include <mach/mach.h>
#include <mach/mach_time.h>
#endif /* OS_DARWIN */
#endif /* OS_UNIX */
#ifdef OS_WIN32
// 21Sep09: windows limits select() to 64 sockets by default, we increase it to 1024 here (before including winsock2.h)
// 18Jun12: fd_setsize must be changed in the ruby binary (not in this extension). redefining it also causes segvs, see eventmachine/eventmachine#333
//#define FD_SETSIZE 1024
// WIN32_LEAN_AND_MEAN excludes APIs such as Cryptography, DDE, RPC, Shell, and Windows Sockets.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <rpc.h>
#include <fcntl.h>
#include <assert.h>
// Older versions of MinGW in the Ruby Dev Kit do not provide the getaddrinfo hint flags
#ifndef AI_ADDRCONFIG
#define AI_ADDRCONFIG 0x0400
#endif
#ifndef AI_NUMERICSERV
#define AI_NUMERICSERV 0x0008
#endif
// Use the Win32 wrapper library that Ruby owns to be able to close sockets with the close() function
#define RUBY_EXPORT
#include <ruby/defines.h>
#include <ruby/win32.h>
#endif /* OS_WIN32 */
#if !defined(_MSC_VER) || _MSC_VER > 1500
#include <stdint.h>
#endif
#ifdef WITH_SSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#endif
#ifdef HAVE_EPOLL
#include <sys/epoll.h>
#endif
#ifdef HAVE_KQUEUE
#include <sys/event.h>
#include <sys/queue.h>
#endif
#ifdef HAVE_INOTIFY
#include <sys/inotify.h>
#endif
#ifdef HAVE_OLD_INOTIFY
#include <sys/syscall.h>
#include <linux/inotify.h>
static inline int inotify_init (void) { return syscall (__NR_inotify_init); }
static inline int inotify_add_watch (int fd, const char *name, __u32 mask) { return syscall (__NR_inotify_add_watch, fd, name, mask); }
static inline int inotify_rm_watch (int fd, __u32 wd) { return syscall (__NR_inotify_rm_watch, fd, wd); }
#define HAVE_INOTIFY 1
#endif
#ifdef HAVE_INOTIFY
#define INOTIFY_EVENT_SIZE (sizeof(struct inotify_event))
#endif
#ifdef HAVE_WRITEV
#include <sys/uio.h>
#endif
#if __cplusplus
extern "C" {
#endif
typedef void (*EMCallback)(const unsigned long, int, const char*, const unsigned long);
#if __cplusplus
}
#endif
#if defined(__GNUC__) && (__GNUC__ >= 3)
#define UNUSED __attribute__ ((unused))
#else
#define UNUSED
#endif
#include "binder.h"
#include "em.h"
#include "ed.h"
#include "page.h"
#include "ssl.h"
#include "eventmachine.h"
#endif // __Project__H_
File diff suppressed because it is too large Load Diff
+619
View File
@@ -0,0 +1,619 @@
/*****************************************************************************
$Id$
File: ssl.cpp
Date: 30Apr06
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
Gmail: blackhedd
This program is free software; you can redistribute it and/or modify
it under the terms of either: 1) 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; or 2) Ruby's License.
See the file COPYING for complete licensing information.
*****************************************************************************/
#ifdef WITH_SSL
#include "project.h"
bool SslContext_t::bLibraryInitialized = false;
static void InitializeDefaultCredentials();
static EVP_PKEY *DefaultPrivateKey = NULL;
static X509 *DefaultCertificate = NULL;
static char PrivateMaterials[] = {
"-----BEGIN RSA PRIVATE KEY-----\n"
"MIICXAIBAAKBgQDCYYhcw6cGRbhBVShKmbWm7UVsEoBnUf0cCh8AX+MKhMxwVDWV\n"
"Igdskntn3cSJjRtmgVJHIK0lpb/FYHQB93Ohpd9/Z18pDmovfFF9nDbFF0t39hJ/\n"
"AqSzFB3GiVPoFFZJEE1vJqh+3jzsSF5K56bZ6azz38VlZgXeSozNW5bXkQIDAQAB\n"
"AoGALA89gIFcr6BIBo8N5fL3aNHpZXjAICtGav+kTUpuxSiaym9cAeTHuAVv8Xgk\n"
"H2Wbq11uz+6JMLpkQJH/WZ7EV59DPOicXrp0Imr73F3EXBfR7t2EQDYHPMthOA1D\n"
"I9EtCzvV608Ze90hiJ7E3guGrGppZfJ+eUWCPgy8CZH1vRECQQDv67rwV/oU1aDo\n"
"6/+d5nqjeW6mWkGqTnUU96jXap8EIw6B+0cUKskwx6mHJv+tEMM2748ZY7b0yBlg\n"
"w4KDghbFAkEAz2h8PjSJG55LwqmXih1RONSgdN9hjB12LwXL1CaDh7/lkEhq0PlK\n"
"PCAUwQSdM17Sl0Xxm2CZiekTSlwmHrtqXQJAF3+8QJwtV2sRJp8u2zVe37IeH1cJ\n"
"xXeHyjTzqZ2803fnjN2iuZvzNr7noOA1/Kp+pFvUZUU5/0G2Ep8zolPUjQJAFA7k\n"
"xRdLkzIx3XeNQjwnmLlncyYPRv+qaE3FMpUu7zftuZBnVCJnvXzUxP3vPgKTlzGa\n"
"dg5XivDRfsV+okY5uQJBAMV4FesUuLQVEKb6lMs7rzZwpeGQhFDRfywJzfom2TLn\n"
"2RdJQQ3dcgnhdVDgt5o1qkmsqQh8uJrJ9SdyLIaZQIc=\n"
"-----END RSA PRIVATE KEY-----\n"
"-----BEGIN CERTIFICATE-----\n"
"MIID6TCCA1KgAwIBAgIJANm4W/Tzs+s+MA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD\n"
"VQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYw\n"
"FAYDVQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsG\n"
"A1UEAxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2lu\n"
"ZWVyaW5nQHN0ZWFtaGVhdC5uZXQwHhcNMDYwNTA1MTcwNjAzWhcNMjQwMjIwMTcw\n"
"NjAzWjCBqjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMREwDwYDVQQH\n"
"EwhOZXcgWW9yazEWMBQGA1UEChMNU3RlYW1oZWF0Lm5ldDEUMBIGA1UECxMLRW5n\n"
"aW5lZXJpbmcxHTAbBgNVBAMTFG9wZW5jYS5zdGVhbWhlYXQubmV0MSgwJgYJKoZI\n"
"hvcNAQkBFhllbmdpbmVlcmluZ0BzdGVhbWhlYXQubmV0MIGfMA0GCSqGSIb3DQEB\n"
"AQUAA4GNADCBiQKBgQDCYYhcw6cGRbhBVShKmbWm7UVsEoBnUf0cCh8AX+MKhMxw\n"
"VDWVIgdskntn3cSJjRtmgVJHIK0lpb/FYHQB93Ohpd9/Z18pDmovfFF9nDbFF0t3\n"
"9hJ/AqSzFB3GiVPoFFZJEE1vJqh+3jzsSF5K56bZ6azz38VlZgXeSozNW5bXkQID\n"
"AQABo4IBEzCCAQ8wHQYDVR0OBBYEFPJvPd1Fcmd8o/Tm88r+NjYPICCkMIHfBgNV\n"
"HSMEgdcwgdSAFPJvPd1Fcmd8o/Tm88r+NjYPICCkoYGwpIGtMIGqMQswCQYDVQQG\n"
"EwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYwFAYD\n"
"VQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsGA1UE\n"
"AxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2luZWVy\n"
"aW5nQHN0ZWFtaGVhdC5uZXSCCQDZuFv087PrPjAMBgNVHRMEBTADAQH/MA0GCSqG\n"
"SIb3DQEBBQUAA4GBAC1CXey/4UoLgJiwcEMDxOvW74plks23090iziFIlGgcIhk0\n"
"Df6hTAs7H3MWww62ddvR8l07AWfSzSP5L6mDsbvq7EmQsmPODwb6C+i2aF3EDL8j\n"
"uw73m4YIGI0Zw2XdBpiOGkx2H56Kya6mJJe/5XORZedh1wpI7zki01tHYbcy\n"
"-----END CERTIFICATE-----\n"};
/* These private materials were made with:
* openssl req -new -x509 -keyout cakey.pem -out cacert.pem -nodes -days 6500
* TODO: We need a full-blown capability to work with user-supplied
* keypairs and properly-signed certificates.
*/
/*****************
builtin_passwd_cb
*****************/
extern "C" int builtin_passwd_cb (char *buf UNUSED, int bufsize UNUSED, int rwflag UNUSED, void *userdata UNUSED)
{
strcpy (buf, "kittycat");
return 8;
}
/****************************
InitializeDefaultCredentials
****************************/
static void InitializeDefaultCredentials()
{
BIO *bio = BIO_new_mem_buf (PrivateMaterials, -1);
assert (bio);
if (DefaultPrivateKey) {
// we may come here in a restart.
EVP_PKEY_free (DefaultPrivateKey);
DefaultPrivateKey = NULL;
}
PEM_read_bio_PrivateKey (bio, &DefaultPrivateKey, builtin_passwd_cb, 0);
if (DefaultCertificate) {
// we may come here in a restart.
X509_free (DefaultCertificate);
DefaultCertificate = NULL;
}
PEM_read_bio_X509 (bio, &DefaultCertificate, NULL, 0);
BIO_free (bio);
}
/**************************
SslContext_t::SslContext_t
**************************/
SslContext_t::SslContext_t (bool is_server, const std::string &privkeyfile, const std::string &certchainfile, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version) :
bIsServer (is_server),
pCtx (NULL),
PrivateKey (NULL),
Certificate (NULL)
{
/* TODO: the usage of the specified private-key and cert-chain filenames only applies to
* client-side connections at this point. Server connections currently use the default materials.
* That needs to be fixed asap.
* Also, in this implementation, server-side connections use statically defined X-509 defaults.
* One thing I'm really not clear on is whether or not you have to explicitly free X509 and EVP_PKEY
* objects when we call our destructor, or whether just calling SSL_CTX_free is enough.
*/
if (!bLibraryInitialized) {
bLibraryInitialized = true;
SSL_library_init();
OpenSSL_add_ssl_algorithms();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
ERR_load_crypto_strings();
InitializeDefaultCredentials();
}
pCtx = SSL_CTX_new (bIsServer ? SSLv23_server_method() : SSLv23_client_method());
if (!pCtx)
throw std::runtime_error ("no SSL context");
SSL_CTX_set_options (pCtx, SSL_OP_ALL);
#ifdef SSL_CTRL_CLEAR_OPTIONS
SSL_CTX_clear_options (pCtx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1);
# ifdef SSL_OP_NO_TLSv1_1
SSL_CTX_clear_options (pCtx, SSL_OP_NO_TLSv1_1);
# endif
# ifdef SSL_OP_NO_TLSv1_2
SSL_CTX_clear_options (pCtx, SSL_OP_NO_TLSv1_2);
# endif
#endif
if (!(ssl_version & EM_PROTO_SSLv2))
SSL_CTX_set_options (pCtx, SSL_OP_NO_SSLv2);
if (!(ssl_version & EM_PROTO_SSLv3))
SSL_CTX_set_options (pCtx, SSL_OP_NO_SSLv3);
if (!(ssl_version & EM_PROTO_TLSv1))
SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1);
#ifdef SSL_OP_NO_TLSv1_1
if (!(ssl_version & EM_PROTO_TLSv1_1))
SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1_1);
#endif
#ifdef SSL_OP_NO_TLSv1_2
if (!(ssl_version & EM_PROTO_TLSv1_2))
SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1_2);
#endif
#ifdef SSL_MODE_RELEASE_BUFFERS
SSL_CTX_set_mode (pCtx, SSL_MODE_RELEASE_BUFFERS);
#endif
if (bIsServer) {
// The SSL_CTX calls here do NOT allocate memory.
int e;
if (privkeyfile.length() > 0)
e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM);
else
e = SSL_CTX_use_PrivateKey (pCtx, DefaultPrivateKey);
if (e <= 0) ERR_print_errors_fp(stderr);
assert (e > 0);
if (certchainfile.length() > 0)
e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str());
else
e = SSL_CTX_use_certificate (pCtx, DefaultCertificate);
if (e <= 0) ERR_print_errors_fp(stderr);
assert (e > 0);
if (dhparam.length() > 0) {
DH *dh;
BIO *bio;
bio = BIO_new_file(dhparam.c_str(), "r");
if (bio == NULL) {
char buf [500];
snprintf (buf, sizeof(buf)-1, "dhparam: BIO_new_file(%s) failed", dhparam.c_str());
throw std::runtime_error (buf);
}
dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
if (dh == NULL) {
BIO_free(bio);
char buf [500];
snprintf (buf, sizeof(buf)-1, "dhparam: PEM_read_bio_DHparams(%s) failed", dhparam.c_str());
throw std::runtime_error (buf);
}
SSL_CTX_set_tmp_dh(pCtx, dh);
DH_free(dh);
BIO_free(bio);
}
if (ecdh_curve.length() > 0) {
#if OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_ECDH)
int nid;
EC_KEY *ecdh;
nid = OBJ_sn2nid((const char *) ecdh_curve.c_str());
if (nid == 0) {
char buf [200];
snprintf (buf, sizeof(buf)-1, "ecdh_curve: Unknown curve name: %s", ecdh_curve.c_str());
throw std::runtime_error (buf);
}
ecdh = EC_KEY_new_by_curve_name(nid);
if (ecdh == NULL) {
char buf [200];
snprintf (buf, sizeof(buf)-1, "ecdh_curve: Unable to create: %s", ecdh_curve.c_str());
throw std::runtime_error (buf);
}
SSL_CTX_set_options(pCtx, SSL_OP_SINGLE_ECDH_USE);
SSL_CTX_set_tmp_ecdh(pCtx, ecdh);
EC_KEY_free(ecdh);
#else
throw std::runtime_error ("No openssl ECDH support");
#endif
}
}
if (cipherlist.length() > 0)
SSL_CTX_set_cipher_list (pCtx, cipherlist.c_str());
else
SSL_CTX_set_cipher_list (pCtx, "ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH");
if (bIsServer) {
SSL_CTX_sess_set_cache_size (pCtx, 128);
SSL_CTX_set_session_id_context (pCtx, (unsigned char*)"eventmachine", 12);
}
else {
int e;
if (privkeyfile.length() > 0) {
e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM);
if (e <= 0) ERR_print_errors_fp(stderr);
assert (e > 0);
}
if (certchainfile.length() > 0) {
e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str());
if (e <= 0) ERR_print_errors_fp(stderr);
assert (e > 0);
}
}
}
/***************************
SslContext_t::~SslContext_t
***************************/
SslContext_t::~SslContext_t()
{
if (pCtx)
SSL_CTX_free (pCtx);
if (PrivateKey)
EVP_PKEY_free (PrivateKey);
if (Certificate)
X509_free (Certificate);
}
/******************
SslBox_t::SslBox_t
******************/
SslBox_t::SslBox_t (bool is_server, const std::string &privkeyfile, const std::string &certchainfile, bool verify_peer, bool fail_if_no_peer_cert, const std::string &snihostname, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version, const uintptr_t binding):
bIsServer (is_server),
bHandshakeCompleted (false),
bVerifyPeer (verify_peer),
bFailIfNoPeerCert (fail_if_no_peer_cert),
pSSL (NULL),
pbioRead (NULL),
pbioWrite (NULL)
{
/* TODO someday: make it possible to re-use SSL contexts so we don't have to create
* a new one every time we come here.
*/
Context = new SslContext_t (bIsServer, privkeyfile, certchainfile, cipherlist, ecdh_curve, dhparam, ssl_version);
assert (Context);
pbioRead = BIO_new (BIO_s_mem());
assert (pbioRead);
pbioWrite = BIO_new (BIO_s_mem());
assert (pbioWrite);
pSSL = SSL_new (Context->pCtx);
assert (pSSL);
if (snihostname.length() > 0) {
SSL_set_tlsext_host_name (pSSL, snihostname.c_str());
}
SSL_set_bio (pSSL, pbioRead, pbioWrite);
// Store a pointer to the binding signature in the SSL object so we can retrieve it later
SSL_set_ex_data(pSSL, 0, (void*) binding);
if (bVerifyPeer) {
int mode = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
if (bFailIfNoPeerCert)
mode = mode | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
SSL_set_verify(pSSL, mode, ssl_verify_wrapper);
}
if (!bIsServer) {
int e = SSL_connect (pSSL);
if (e != 1)
ERR_print_errors_fp(stderr);
}
}
/*******************
SslBox_t::~SslBox_t
*******************/
SslBox_t::~SslBox_t()
{
// Freeing pSSL will also free the associated BIOs, so DON'T free them separately.
if (pSSL) {
if (SSL_get_shutdown (pSSL) & SSL_RECEIVED_SHUTDOWN)
SSL_shutdown (pSSL);
else
SSL_clear (pSSL);
SSL_free (pSSL);
}
delete Context;
}
/***********************
SslBox_t::PutCiphertext
***********************/
bool SslBox_t::PutCiphertext (const char *buf, int bufsize)
{
assert (buf && (bufsize > 0));
assert (pbioRead);
int n = BIO_write (pbioRead, buf, bufsize);
return (n == bufsize) ? true : false;
}
/**********************
SslBox_t::GetPlaintext
**********************/
int SslBox_t::GetPlaintext (char *buf, int bufsize)
{
if (!SSL_is_init_finished (pSSL)) {
int e = bIsServer ? SSL_accept (pSSL) : SSL_connect (pSSL);
if (e != 1) {
int er = SSL_get_error (pSSL, e);
if (er != SSL_ERROR_WANT_READ) {
ERR_print_errors_fp(stderr);
// Return -1 for a nonfatal error, -2 for an error that should force the connection down.
return (er == SSL_ERROR_SSL) ? (-2) : (-1);
}
else
return 0;
}
bHandshakeCompleted = true;
// If handshake finished, FALL THROUGH and return the available plaintext.
}
if (!SSL_is_init_finished (pSSL)) {
// We can get here if a browser abandons a handshake.
// The user can see a warning dialog and abort the connection.
//cerr << "<SSL_incomp>";
return 0;
}
//cerr << "CIPH: " << SSL_get_cipher (pSSL) << endl;
int n = SSL_read (pSSL, buf, bufsize);
if (n >= 0) {
return n;
}
else {
if (SSL_get_error (pSSL, n) == SSL_ERROR_WANT_READ) {
return 0;
}
else {
return -1;
}
}
return 0;
}
/**************************
SslBox_t::CanGetCiphertext
**************************/
bool SslBox_t::CanGetCiphertext()
{
assert (pbioWrite);
return BIO_pending (pbioWrite) ? true : false;
}
/***********************
SslBox_t::GetCiphertext
***********************/
int SslBox_t::GetCiphertext (char *buf, int bufsize)
{
assert (pbioWrite);
assert (buf && (bufsize > 0));
return BIO_read (pbioWrite, buf, bufsize);
}
/**********************
SslBox_t::PutPlaintext
**********************/
int SslBox_t::PutPlaintext (const char *buf, int bufsize)
{
// The caller will interpret the return value as the number of bytes written.
// WARNING WARNING WARNING, are there any situations in which a 0 or -1 return
// from SSL_write means we should immediately retry? The socket-machine loop
// will probably wait for a time-out cycle (perhaps a second) before re-trying.
// THIS WOULD CAUSE A PERCEPTIBLE DELAY!
/* We internally queue any outbound plaintext that can't be dispatched
* because we're in the middle of a handshake or something.
* When we get called, try to send any queued data first, and then
* send the caller's data (or queue it). We may get called with no outbound
* data, which means we try to send the outbound queue and that's all.
*
* Return >0 if we wrote any data, 0 if we didn't, and <0 for a fatal error.
* Note that if we return 0, the connection is still considered live
* and we are signalling that we have accepted the outbound data (if any).
*/
OutboundQ.Push (buf, bufsize);
if (!SSL_is_init_finished (pSSL))
return 0;
bool fatal = false;
bool did_work = false;
int pending = BIO_pending(pbioWrite);
while (OutboundQ.HasPages() && pending < SSLBOX_WRITE_BUFFER_SIZE) {
const char *page;
int length;
OutboundQ.Front (&page, &length);
assert (page && (length > 0));
int n = SSL_write (pSSL, page, length);
pending = BIO_pending(pbioWrite);
if (n > 0) {
did_work = true;
OutboundQ.PopFront();
}
else {
int er = SSL_get_error (pSSL, n);
if ((er != SSL_ERROR_WANT_READ) && (er != SSL_ERROR_WANT_WRITE))
fatal = true;
break;
}
}
if (did_work)
return 1;
else if (fatal)
return -1;
else
return 0;
}
/**********************
SslBox_t::GetPeerCert
**********************/
X509 *SslBox_t::GetPeerCert()
{
X509 *cert = NULL;
if (pSSL)
cert = SSL_get_peer_certificate(pSSL);
return cert;
}
/**********************
SslBox_t::GetCipherBits
**********************/
int SslBox_t::GetCipherBits()
{
int bits = -1;
if (pSSL)
SSL_get_cipher_bits(pSSL, &bits);
return bits;
}
/**********************
SslBox_t::GetCipherName
**********************/
const char *SslBox_t::GetCipherName()
{
if (pSSL)
return SSL_get_cipher_name(pSSL);
return NULL;
}
/**********************
SslBox_t::GetCipherProtocol
**********************/
const char *SslBox_t::GetCipherProtocol()
{
if (pSSL)
return SSL_get_cipher_version(pSSL);
return NULL;
}
/**********************
SslBox_t::GetSNIHostname
**********************/
const char *SslBox_t::GetSNIHostname()
{
#ifdef TLSEXT_NAMETYPE_host_name
if (pSSL)
return SSL_get_servername (pSSL, TLSEXT_NAMETYPE_host_name);
#endif
return NULL;
}
/******************
ssl_verify_wrapper
*******************/
extern "C" int ssl_verify_wrapper(int preverify_ok UNUSED, X509_STORE_CTX *ctx)
{
uintptr_t binding;
X509 *cert;
SSL *ssl;
BUF_MEM *buf;
BIO *out;
int result;
cert = X509_STORE_CTX_get_current_cert(ctx);
ssl = (SSL*) X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
binding = (uintptr_t) SSL_get_ex_data(ssl, 0);
out = BIO_new(BIO_s_mem());
PEM_write_bio_X509(out, cert);
BIO_write(out, "\0", 1);
BIO_get_mem_ptr(out, &buf);
ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject(binding));
result = (cd->VerifySslPeer(buf->data) == true ? 1 : 0);
BIO_free(out);
return result;
}
#endif // WITH_SSL
+103
View File
@@ -0,0 +1,103 @@
/*****************************************************************************
$Id$
File: ssl.h
Date: 30Apr06
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
Gmail: blackhedd
This program is free software; you can redistribute it and/or modify
it under the terms of either: 1) 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; or 2) Ruby's License.
See the file COPYING for complete licensing information.
*****************************************************************************/
#ifndef __SslBox__H_
#define __SslBox__H_
#ifdef WITH_SSL
/******************
class SslContext_t
******************/
class SslContext_t
{
public:
SslContext_t (bool is_server, const std::string &privkeyfile, const std::string &certchainfile, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version);
virtual ~SslContext_t();
private:
static bool bLibraryInitialized;
private:
bool bIsServer;
SSL_CTX *pCtx;
EVP_PKEY *PrivateKey;
X509 *Certificate;
friend class SslBox_t;
};
/**************
class SslBox_t
**************/
#define SSLBOX_INPUT_CHUNKSIZE 2019
#define SSLBOX_OUTPUT_CHUNKSIZE 2048
#define SSLBOX_WRITE_BUFFER_SIZE 8192 // (SSLBOX_OUTPUT_CHUNKSIZE * 4)
class SslBox_t
{
public:
SslBox_t (bool is_server, const std::string &privkeyfile, const std::string &certchainfile, bool verify_peer, bool fail_if_no_peer_cert, const std::string &snihostname, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version, const uintptr_t binding);
virtual ~SslBox_t();
int PutPlaintext (const char*, int);
int GetPlaintext (char*, int);
bool PutCiphertext (const char*, int);
bool CanGetCiphertext();
int GetCiphertext (char*, int);
bool IsHandshakeCompleted() {return bHandshakeCompleted;}
X509 *GetPeerCert();
int GetCipherBits();
const char *GetCipherName();
const char *GetCipherProtocol();
const char *GetSNIHostname();
void Shutdown();
protected:
SslContext_t *Context;
bool bIsServer;
bool bHandshakeCompleted;
bool bVerifyPeer;
bool bFailIfNoPeerCert;
SSL *pSSL;
BIO *pbioRead;
BIO *pbioWrite;
PageList OutboundQ;
};
extern "C" int ssl_verify_wrapper(int, X509_STORE_CTX*);
#endif // WITH_SSL
#endif // __SslBox__H_