2012-05-09 14:05:03 -05:00
/*
* Copyright ( c ) 2012 - 2013 , Anthony Minessale II
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
*
* * Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
*
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
*
* * Neither the name of the original author ; nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission .
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
* LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL ,
* EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO ,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR
* PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING
* NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
# include <scgi.h>
# ifndef WIN32
# include <fcntl.h>
# include <errno.h>
# else
# pragma warning (disable:6386)
/* These warnings need to be ignored warning in sdk header */
# include <Ws2tcpip.h>
# include <windows.h>
# ifndef errno
# define errno WSAGetLastError()
# endif
# ifndef EINTR
# define EINTR WSAEINTR
# endif
# pragma warning (default:6386)
# endif
static scgi_status_t scgi_push_param ( scgi_handle_t * handle , const char * name , const char * value ) ;
static int sock_setup ( scgi_handle_t * handle )
{
if ( handle - > sock = = SCGI_SOCK_INVALID ) {
return SCGI_FAIL ;
}
# ifdef WIN32
{
BOOL bOptVal = TRUE ;
int bOptLen = sizeof ( BOOL ) ;
setsockopt ( handle - > sock , IPPROTO_TCP , TCP_NODELAY , ( const char * ) & bOptVal , bOptLen ) ;
}
# else
{
int x = 1 ;
setsockopt ( handle - > sock , IPPROTO_TCP , TCP_NODELAY , & x , sizeof ( x ) ) ;
}
# endif
return SCGI_SUCCESS ;
}
SCGI_DECLARE ( size_t ) scgi_build_message ( scgi_handle_t * handle , char * * bufferp )
{
scgi_param_t * pp ;
size_t len = 0 , plen = 0 , ctlen = 0 ;
char * s , * bp ;
char * buffer = NULL ;
char tmp [ 128 ] = " " ;
scgi_push_param ( handle , " SCGI " , " 1 " ) ;
if ( handle - > body ) {
ctlen = strlen ( handle - > body ) ;
}
2012-05-14 09:47:35 -05:00
snprintf ( tmp , sizeof ( tmp ) , " %d " , ( int ) ctlen ) ;
2012-05-09 14:05:03 -05:00
scgi_push_param ( handle , " CONTENT_LENGTH " , tmp ) ;
for ( pp = handle - > params ; pp ; pp = pp - > next ) {
plen + = ( strlen ( pp - > name ) + strlen ( pp - > value ) + 2 ) ;
}
2012-05-14 09:47:35 -05:00
snprintf ( tmp , sizeof ( tmp ) , " %d " , ( int ) ( plen + ctlen ) ) ;
2012-05-09 14:05:03 -05:00
len = plen + ctlen + strlen ( tmp ) + 2 ;
buffer = malloc ( len ) ;
memset ( buffer , 0 , len ) ;
2012-05-14 09:47:35 -05:00
snprintf ( buffer , len , " %d: " , ( int ) plen ) ;
2012-05-09 14:05:03 -05:00
bp = buffer + strlen ( buffer ) ;
for ( pp = handle - > params ; pp ; pp = pp - > next ) {
for ( s = pp - > name ; s & & * s ; s + + ) {
* bp + + = * s ;
}
* bp + + = ' \0 ' ;
for ( s = pp - > value ; s & & * s ; s + + ) {
* bp + + = * s ;
}
* bp + + = ' \0 ' ;
}
* bp + + = ' , ' ;
if ( handle - > body ) {
for ( s = handle - > body ; s & & * s ; s + + ) {
* bp + + = * s ;
}
}
* bufferp = buffer ;
return len ;
}
SCGI_DECLARE ( scgi_status_t ) scgi_destroy_params ( scgi_handle_t * handle )
{
scgi_param_t * param , * pp ;
pp = handle - > params ;
while ( pp ) {
param = pp ;
pp = pp - > next ;
free ( param - > name ) ;
free ( param - > value ) ;
free ( param ) ;
}
handle - > params = NULL ;
return SCGI_SUCCESS ;
}
SCGI_DECLARE ( scgi_status_t ) scgi_add_body ( scgi_handle_t * handle , const char * value )
{
handle - > body = strdup ( value ) ;
return SCGI_SUCCESS ;
}
2012-05-10 08:23:23 -05:00
SCGI_DECLARE ( const char * ) scgi_get_body ( scgi_handle_t * handle )
{
return handle - > body ;
}
2012-05-09 14:05:03 -05:00
SCGI_DECLARE ( scgi_status_t ) scgi_add_param ( scgi_handle_t * handle , const char * name , const char * value )
{
scgi_param_t * param , * pp ;
for ( pp = handle - > params ; pp & & pp - > next ; pp = pp - > next ) {
if ( ! strcasecmp ( pp - > name , name ) ) {
return SCGI_FAIL ;
}
}
param = malloc ( sizeof ( * param ) ) ;
memset ( param , 0 , sizeof ( * param ) ) ;
param - > name = strdup ( name ) ;
param - > value = strdup ( value ) ;
if ( ! pp ) {
handle - > params = param ;
} else {
pp - > next = param ;
}
return SCGI_SUCCESS ;
}
2012-05-10 08:23:23 -05:00
SCGI_DECLARE ( const char * ) scgi_get_param ( scgi_handle_t * handle , const char * name )
{
scgi_param_t * pp ;
for ( pp = handle - > params ; pp ; pp = pp - > next ) {
if ( ! strcasecmp ( pp - > name , name ) ) {
return pp - > value ;
}
}
return NULL ;
}
2012-05-09 14:05:03 -05:00
static scgi_status_t scgi_push_param ( scgi_handle_t * handle , const char * name , const char * value )
{
scgi_param_t * param ;
param = malloc ( sizeof ( * param ) ) ;
memset ( param , 0 , sizeof ( * param ) ) ;
param - > name = strdup ( name ) ;
param - > value = strdup ( value ) ;
param - > next = handle - > params ;
handle - > params = param ;
return SCGI_SUCCESS ;
}
SCGI_DECLARE ( scgi_status_t ) scgi_send_request ( scgi_handle_t * handle )
{
2012-05-10 11:01:30 -05:00
scgi_status_t status = SCGI_SUCCESS ;
2012-05-09 14:05:03 -05:00
char * buffer = NULL ;
size_t bytes = 0 ;
ssize_t sent = 0 ;
if ( handle - > connected ! = 1 ) {
return SCGI_FAIL ;
}
bytes = scgi_build_message ( handle , & buffer ) ;
sent = send ( handle - > sock , buffer , bytes , 0 ) ;
if ( sent < = 0 ) {
handle - > connected = - 1 ;
}
scgi_safe_free ( buffer ) ;
return status ;
}
SCGI_DECLARE ( ssize_t ) scgi_recv ( scgi_handle_t * handle , unsigned char * buf , size_t buflen )
{
ssize_t recvd ;
if ( handle - > connected ! = 1 ) {
return - 1 ;
}
recvd = recv ( handle - > sock , buf , buflen , 0 ) ;
if ( recvd = = 0 ) {
handle - > connected = - 1 ;
}
return recvd ;
}
SCGI_DECLARE ( scgi_status_t ) scgi_connect ( scgi_handle_t * handle , const char * host , scgi_port_t port , uint32_t timeout )
{
int rval = 0 ;
struct addrinfo hints = { 0 } , * result ;
# ifndef WIN32
int fd_flags = 0 ;
# else
WORD wVersionRequested = MAKEWORD ( 2 , 0 ) ;
WSADATA wsaData ;
int err = WSAStartup ( wVersionRequested , & wsaData ) ;
if ( err ! = 0 ) {
snprintf ( handle - > err , sizeof ( handle - > err ) , " WSAStartup Error " ) ;
return SCGI_FAIL ;
}
# endif
handle - > sock = socket ( PF_INET , SOCK_STREAM , IPPROTO_TCP ) ;
if ( handle - > sock = = SCGI_SOCK_INVALID ) {
snprintf ( handle - > err , sizeof ( handle - > err ) , " Socket Error " ) ;
return SCGI_FAIL ;
}
hints . ai_family = AF_INET ;
hints . ai_socktype = SOCK_STREAM ;
if ( getaddrinfo ( host , NULL , & hints , & result ) ) {
strncpy ( handle - > err , " Cannot resolve host " , sizeof ( handle - > err ) ) ;
goto fail ;
}
memcpy ( & handle - > sockaddr , result - > ai_addr , sizeof ( handle - > sockaddr ) ) ;
handle - > sockaddr . sin_family = AF_INET ;
handle - > sockaddr . sin_port = htons ( port ) ;
freeaddrinfo ( result ) ;
if ( timeout ) {
# ifdef WIN32
u_long arg = 1 ;
if ( ioctlsocket ( handle - > sock , FIONBIO , & arg ) = = SOCKET_ERROR ) {
snprintf ( handle - > err , sizeof ( handle - > err ) , " Socket Connection Error " ) ;
goto fail ;
}
# else
fd_flags = fcntl ( handle - > sock , F_GETFL , 0 ) ;
if ( fcntl ( handle - > sock , F_SETFL , fd_flags | O_NONBLOCK ) ) {
snprintf ( handle - > err , sizeof ( handle - > err ) , " Socket Connection Error " ) ;
goto fail ;
}
# endif
}
rval = connect ( handle - > sock , ( struct sockaddr * ) & handle - > sockaddr , sizeof ( handle - > sockaddr ) ) ;
if ( timeout ) {
int r ;
r = scgi_wait_sock ( handle - > sock , timeout , SCGI_POLL_WRITE ) ;
if ( r < = 0 ) {
snprintf ( handle - > err , sizeof ( handle - > err ) , " Connection timed out " ) ;
goto fail ;
}
if ( ! ( r & SCGI_POLL_WRITE ) ) {
snprintf ( handle - > err , sizeof ( handle - > err ) , " Connection timed out " ) ;
goto fail ;
}
# ifdef WIN32
{
u_long arg = 0 ;
if ( ioctlsocket ( handle - > sock , FIONBIO , & arg ) = = SOCKET_ERROR ) {
snprintf ( handle - > err , sizeof ( handle - > err ) , " Socket Connection Error " ) ;
goto fail ;
}
}
# else
fcntl ( handle - > sock , F_SETFL , fd_flags ) ;
# endif
rval = 0 ;
}
result = NULL ;
if ( rval ) {
snprintf ( handle - > err , sizeof ( handle - > err ) , " Socket Connection Error " ) ;
goto fail ;
}
sock_setup ( handle ) ;
handle - > connected = 1 ;
return SCGI_SUCCESS ;
fail :
handle - > connected = 0 ;
scgi_disconnect ( handle ) ;
return SCGI_FAIL ;
}
SCGI_DECLARE ( scgi_status_t ) scgi_disconnect ( scgi_handle_t * handle )
{
scgi_status_t status = SCGI_FAIL ;
if ( handle - > destroyed ) {
return SCGI_FAIL ;
}
if ( handle - > sock ! = SCGI_SOCK_INVALID ) {
closesocket ( handle - > sock ) ;
handle - > sock = SCGI_SOCK_INVALID ;
status = SCGI_SUCCESS ;
}
2012-05-23 11:31:17 -05:00
handle - > destroyed = 1 ;
handle - > connected = 0 ;
scgi_destroy_params ( handle ) ;
scgi_safe_free ( handle - > body ) ;
2012-05-09 14:05:03 -05:00
return status ;
}
/* USE WSAPoll on vista or higher */
# ifdef SCGI_USE_WSAPOLL
SCGI_DECLARE ( int ) scgi_wait_sock ( scgi_socket_t sock , uint32_t ms , scgi_poll_t flags )
{
}
# endif
# ifdef SCGI_USE_SELECT
# ifdef WIN32
# pragma warning( push )
# pragma warning( disable : 6262 ) /* warning C6262: Function uses '98348' bytes of stack: exceeds /analyze:stacksize'16384'. Consider moving some data to heap */
# endif
SCGI_DECLARE ( int ) scgi_wait_sock ( scgi_socket_t sock , uint32_t ms , scgi_poll_t flags )
{
int s = 0 , r = 0 ;
fd_set rfds ;
fd_set wfds ;
fd_set efds ;
struct timeval tv ;
FD_ZERO ( & rfds ) ;
FD_ZERO ( & wfds ) ;
FD_ZERO ( & efds ) ;
# ifndef WIN32
/* Wouldn't you rather know?? */
assert ( sock < = FD_SETSIZE ) ;
# endif
if ( ( flags & SCGI_POLL_READ ) ) {
# ifdef WIN32
# pragma warning( push )
# pragma warning( disable : 4127 )
FD_SET ( sock , & rfds ) ;
# pragma warning( pop )
# else
FD_SET ( sock , & rfds ) ;
# endif
}
if ( ( flags & SCGI_POLL_WRITE ) ) {
# ifdef WIN32
# pragma warning( push )
# pragma warning( disable : 4127 )
FD_SET ( sock , & wfds ) ;
# pragma warning( pop )
# else
FD_SET ( sock , & wfds ) ;
# endif
}
if ( ( flags & SCGI_POLL_ERROR ) ) {
# ifdef WIN32
# pragma warning( push )
# pragma warning( disable : 4127 )
FD_SET ( sock , & efds ) ;
# pragma warning( pop )
# else
FD_SET ( sock , & efds ) ;
# endif
}
tv . tv_sec = ms / 1000 ;
tv . tv_usec = ( ms % 1000 ) * ms ;
s = select ( sock + 1 , ( flags & SCGI_POLL_READ ) ? & rfds : NULL , ( flags & SCGI_POLL_WRITE ) ? & wfds : NULL , ( flags & SCGI_POLL_ERROR ) ? & efds : NULL , & tv ) ;
if ( s < 0 ) {
r = s ;
} else if ( s > 0 ) {
if ( ( flags & SCGI_POLL_READ ) & & FD_ISSET ( sock , & rfds ) ) {
r | = SCGI_POLL_READ ;
}
if ( ( flags & SCGI_POLL_WRITE ) & & FD_ISSET ( sock , & wfds ) ) {
r | = SCGI_POLL_WRITE ;
}
if ( ( flags & SCGI_POLL_ERROR ) & & FD_ISSET ( sock , & efds ) ) {
r | = SCGI_POLL_ERROR ;
}
}
return r ;
}
# ifdef WIN32
# pragma warning( pop )
# endif
# endif
# ifdef SCGI_USE_POLL
SCGI_DECLARE ( int ) scgi_wait_sock ( scgi_socket_t sock , uint32_t ms , scgi_poll_t flags )
{
struct pollfd pfds [ 2 ] = { { 0 } } ;
int s = 0 , r = 0 ;
pfds [ 0 ] . fd = sock ;
if ( ( flags & SCGI_POLL_READ ) ) {
pfds [ 0 ] . events | = POLLIN ;
}
if ( ( flags & SCGI_POLL_WRITE ) ) {
pfds [ 0 ] . events | = POLLOUT ;
}
if ( ( flags & SCGI_POLL_ERROR ) ) {
pfds [ 0 ] . events | = POLLERR ;
}
s = poll ( pfds , 1 , ms ) ;
if ( s < 0 ) {
r = s ;
} else if ( s > 0 ) {
if ( ( pfds [ 0 ] . revents & POLLIN ) ) {
r | = SCGI_POLL_READ ;
}
if ( ( pfds [ 0 ] . revents & POLLOUT ) ) {
r | = SCGI_POLL_WRITE ;
}
if ( ( pfds [ 0 ] . revents & POLLERR ) ) {
r | = SCGI_POLL_ERROR ;
}
}
return r ;
}
# endif
2012-05-10 08:23:23 -05:00
static int scgi_socket_reuseaddr ( scgi_socket_t socket )
{
# ifdef WIN32
BOOL reuse_addr = TRUE ;
return setsockopt ( socket , SOL_SOCKET , SO_REUSEADDR , ( char * ) & reuse_addr , sizeof ( reuse_addr ) ) ;
# else
int reuse_addr = 1 ;
return setsockopt ( socket , SOL_SOCKET , SO_REUSEADDR , & reuse_addr , sizeof ( reuse_addr ) ) ;
# endif
}
2012-05-23 09:37:54 -05:00
SCGI_DECLARE ( scgi_status_t ) scgi_bind ( const char * host , scgi_port_t port , scgi_socket_t * socketp )
2012-05-10 08:23:23 -05:00
{
scgi_socket_t server_sock = SCGI_SOCK_INVALID ;
struct sockaddr_in addr ;
scgi_status_t status = SCGI_SUCCESS ;
if ( ( server_sock = socket ( PF_INET , SOCK_STREAM , IPPROTO_TCP ) ) < 0 ) {
return SCGI_FAIL ;
}
scgi_socket_reuseaddr ( server_sock ) ;
memset ( & addr , 0 , sizeof ( addr ) ) ;
addr . sin_family = AF_INET ;
addr . sin_addr . s_addr = htonl ( INADDR_ANY ) ;
addr . sin_port = htons ( port ) ;
if ( bind ( server_sock , ( struct sockaddr * ) & addr , sizeof ( addr ) ) < 0 ) {
status = SCGI_FAIL ;
goto end ;
}
if ( listen ( server_sock , 10000 ) < 0 ) {
status = SCGI_FAIL ;
goto end ;
2012-05-23 09:37:54 -05:00
}
end :
2012-05-23 11:31:17 -05:00
if ( status = = SCGI_FAIL ) {
if ( server_sock ! = SCGI_SOCK_INVALID ) {
closesocket ( server_sock ) ;
}
2012-05-23 09:37:54 -05:00
} else {
* socketp = server_sock ;
2012-05-10 08:23:23 -05:00
}
2012-05-23 09:37:54 -05:00
return status ;
}
SCGI_DECLARE ( scgi_status_t ) scgi_accept ( scgi_socket_t server_sock , scgi_socket_t * client_sock_p , struct sockaddr_in * echoClntAddr )
{
scgi_status_t status = SCGI_SUCCESS ;
int client_sock ;
struct sockaddr_in local_echoClntAddr ;
2012-05-10 08:23:23 -05:00
# ifdef WIN32
2012-05-23 09:37:54 -05:00
int clntLen ;
2012-05-10 08:23:23 -05:00
# else
2012-05-23 09:37:54 -05:00
unsigned int clntLen ;
2012-05-10 08:23:23 -05:00
# endif
2012-05-23 09:37:54 -05:00
if ( ! echoClntAddr ) {
echoClntAddr = & local_echoClntAddr ;
}
2012-05-23 11:31:17 -05:00
2012-05-23 09:37:54 -05:00
clntLen = sizeof ( * echoClntAddr ) ;
2012-05-10 08:23:23 -05:00
2012-05-23 09:37:54 -05:00
if ( ( client_sock = accept ( server_sock , ( struct sockaddr * ) echoClntAddr , & clntLen ) ) = = SCGI_SOCK_INVALID ) {
2012-07-14 20:23:13 -05:00
//printf("FRICK %s\n", strerror(errno));
2012-05-23 09:37:54 -05:00
status = SCGI_FAIL ;
} else {
* client_sock_p = client_sock ;
2012-05-10 08:23:23 -05:00
}
2012-05-23 09:37:54 -05:00
return status ;
}
2012-05-10 08:23:23 -05:00
2012-05-23 09:37:54 -05:00
SCGI_DECLARE ( scgi_status_t ) scgi_listen ( const char * host , scgi_port_t port , scgi_listen_callback_t callback )
{
2012-05-10 08:23:23 -05:00
2012-05-23 09:37:54 -05:00
scgi_socket_t server_sock = SCGI_SOCK_INVALID , client_sock = SCGI_SOCK_INVALID ;
scgi_status_t status = SCGI_FAIL ;
struct sockaddr_in echoClntAddr ;
if ( ( status = scgi_bind ( host , port , & server_sock ) ) = = SCGI_SUCCESS ) {
while ( scgi_accept ( server_sock , & client_sock , & echoClntAddr ) = = SCGI_SUCCESS ) {
callback ( server_sock , & client_sock , & echoClntAddr ) ;
if ( client_sock ! = SCGI_SOCK_INVALID ) {
closesocket ( client_sock ) ;
client_sock = SCGI_SOCK_INVALID ;
}
}
2012-05-10 08:23:23 -05:00
}
return status ;
}
# define next_str(_e, _s) _s = _e; while(*_s) _s++; _s++
SCGI_DECLARE ( scgi_status_t ) scgi_parse ( scgi_socket_t sock , scgi_handle_t * handle )
{
char sbuf [ 128 ] = " " ;
char * p = sbuf , * e , * end ;
ssize_t r = 0 ;
scgi_status_t status = SCGI_FAIL ;
ssize_t bytes = 0 ;
char * headers = NULL ;
int loops = 0 ;
ssize_t clen = 0 ;
char * body = NULL ;
char comma = 0 ;
2012-05-23 11:31:17 -05:00
memset ( handle , 0 , sizeof ( * handle ) ) ;
2012-05-10 08:23:23 -05:00
handle - > sock = sock ;
2012-05-23 11:31:17 -05:00
2012-05-10 08:23:23 -05:00
handle - > connected = 1 ;
sock_setup ( handle ) ;
for ( ; ; ) {
if ( ( r = recv ( sock , p , 1 , 0 ) ) < 1 ) {
break ;
}
if ( * p = = ' : ' ) {
* p = ' \0 ' ;
break ;
}
p + + ;
}
if ( r < = 0 ) goto end ;
bytes = atoi ( sbuf ) ;
if ( bytes < = 0 ) goto end ;
headers = malloc ( bytes ) ;
r = recv ( sock , headers , bytes , 0 ) ;
if ( r < = 0 ) goto end ;
r = recv ( sock , & comma , 1 , 0 ) ;
if ( r < = 0 | | comma ! = ' , ' ) goto end ;
p = headers ;
end = p + bytes ;
e = NULL ;
while ( p < end ) {
next_str ( p , e ) ;
if ( ! loops + + ) {
if ( ! strcasecmp ( p , " CONTENT_LENGTH " ) & & e ) {
clen = atoi ( e ) ;
if ( clen ) {
body = malloc ( clen + 1 ) ;
r = recv ( sock , body , clen , 0 ) ;
* ( body + clen ) = ' \0 ' ;
if ( r < = 0 ) goto end ;
scgi_add_body ( handle , body ) ;
scgi_safe_free ( body ) ;
}
status = SCGI_SUCCESS ;
} else {
goto end ;
}
}
scgi_add_param ( handle , p , e ) ;
next_str ( e , p ) ;
}
end :
scgi_safe_free ( headers ) ;
2014-05-07 12:50:53 -04:00
scgi_safe_free ( body ) ;
2012-05-10 08:23:23 -05:00
return status ;
}
/* For Emacs:
* Local Variables :
* mode : c
* indent - tabs - mode : t
* tab - width : 4
* c - basic - offset : 4
* End :
* For VIM :
2013-06-25 11:50:17 -05:00
* vim : set softtabstop = 4 shiftwidth = 4 tabstop = 4 noet :
2012-05-10 08:23:23 -05:00
*/