-/**
- * LIBPING socket library
- *
- * Copyright (C) 2000, 2001, 2002 by
- * Jeffrey Fulmer - <jdfulmer@armstrong.com>
- * This file is distributed as part of libping
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif/*HAVE_CONFIG_H*/
-
-#include <stdio.h>
-#include <sock.h>
-#include <fcntl.h>
-
-#ifdef HAVE_ARPA_INET_H
-# include <arpa/inet.h>
-#endif/*HAVE_ARPA_INET_H*/
-
-#ifdef HAVE_SYS_SOCKET_H
-# include <sys/socket.h>
-#endif/*HAVE_SYS_SOCKET_H*/
-
-#ifdef HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif/*HAVE_NETINET_IN_H*/
-
-#ifdef HAVE_NETDB_H
-# include <netdb.h>
-#endif/*HAVE_NETDB_H*/
-
-#ifdef HAVE_SSL
-# include <openssl/rand.h>
-#endif/*HAVE_SSL */
-
-#include <setup.h>
-#include <errno.h>
-#include <string.h>
-
-/**
- * local prototypes
- */
-int mknblock( int socket, int nonblock );
-ssize_t socket_write( int sock, const void *vbuf, size_t len );
-ssize_t ssl_socket_write( CONN *C, const void *vbuf, size_t len );
-
-/**
- * JOEsocket
- * returns int, socket handle
- */
-int
-JOEsocket( CONN *C, const char *hn )
-{
- int conn;
- struct timeval timeout;
- int res;
- int herrno;
- int error;
- socklen_t len;
- struct linger ling;
- struct sockaddr_in cli;
- struct hostent *hp;
- struct hostent hent;
- char hbf[9000];
-
-#if defined(_AIX)
- char *aixbuf;
- int rc;
-#endif/*_AIX*/
-
-#ifdef HAVE_SSL
- int serr;
- char buf[1024];
- C->ssl = NULL;
- C->ctx = NULL;
-#endif/*HAVE_SSL*/
-
- C->sock = -1;
-
- if( C->prot == HTTPS ){
- #ifdef HAVE_SSL
- SSL_load_error_strings();
- SSLeay_add_ssl_algorithms();
- C->ctx = SSL_CTX_new( SSLv3_client_method());
- if( C->ctx == NULL ){ perror( "SSL: ctx is NULL" ); }
- /* http://www.openssl.org/support/faq.html#USER1
- * Perhaps someday I'll learn to read the FAQ
- * first and then code second, but probably not.
- * Not all OSes have /dev/urandom, we must seed
- * the PRNG
- */
- memset( buf, 0, sizeof( buf ));
- RAND_seed( buf, sizeof( buf ));
- C->ssl = SSL_new( C->ctx );
- SSL_set_connect_state( C->ssl );
- #else
- return -1;
- #endif /* HAVE_SSL */
- }
-
- /* create a socket, return -1 on failure */
- if(( C->sock = socket( AF_INET, SOCK_STREAM, 0 )) < 0 ){
- return -1;
- }
-
-#if defined(__GLIBC__)
- /* for systems using GNU libc */
- if(( gethostbyname_r( hn, &hent, hbf, sizeof(hbf), &hp, &herrno ) < 0 )){
- hp = NULL;
- }
-#elif defined(sun)
- /* Solaris 5++ */
- hp = gethostbyname_r( hn, &hent, hbf, sizeof(hbf), &herrno );
-#elif defined(_AIX)
- aixbuf = (char*)xmalloc( 9000 );
- rc = gethostbyname_r(hn, (struct hostent *)aixbuf,
- (struct hostent_data *)(aixbuf + sizeof(struct hostent)));
- hp = (struct hostent*)aixbuf;
-#elif ( defined(hpux) || defined(__osf__) )
- hp = gethostbyname( hn );
- herrno = h_errno;
-#else
- /* simply hoping that gethostbyname is thread-safe */
- hp = gethostbyname( hn );
- herrno = h_errno;
-#endif/*OS SPECIFICS*/
-
- if( hp == NULL ) return -1;
- bzero( &cli, sizeof( cli ));
- bcopy( (char*)hp->h_addr, (char*)&cli.sin_addr, hp->h_length );
- cli.sin_family = AF_INET;
- cli.sin_port = htons( C->port );
-
- ling.l_onoff = 1;
- ling.l_linger = 0;
- if( setsockopt( C->sock, SOL_SOCKET, SO_LINGER, &ling, sizeof( ling )) < 0 ){
- return -1;
- }
-
- /**
- * connect to the host
- * evaluate the server response and check for
- * readability/writeability of the socket....
- */
- conn = connect( C->sock, (struct sockaddr *)&cli, sizeof(struct sockaddr_in));
- if( conn < 0 ){
- JOEclose( C );
- return -1;
- }
- else if( conn == 0 ){
- goto FINISH; /* immediate connection */
- }
- else{
- /**
- * we have a connection, set the
- * socket to non-blocking and test
- * its integrity.
- */
- if(( mknblock( C->sock, TRUE )) < 0 ){
- return -1;
- }
- FD_ZERO(&C->rset);
- FD_ZERO(&C->wset);
- FD_SET( C->sock, &C->rset );
- FD_SET( C->sock, &C->wset );
- memset((void *)&timeout, '\0', sizeof( struct timeval ));
- /**
- * the default timeout is set in init.c, it's
- * value can be changed by the user in .siegerc,
- * but we'll still use a ternary condition since
- * you can't be too safe....
- */
- timeout.tv_sec = (C->timeout > 0)?C->timeout:60;
- timeout.tv_usec = 0;
-
- if(( res = select( C->sock+1, &C->rset, &C->wset, NULL, &timeout )) < 1 ){
- perror( "JOEsocket: connection timed out." );
- close( C->sock );
- return -1;
- }
- if( FD_ISSET( C->sock, &C->rset) || FD_ISSET( C->sock, &C->wset )){
- len = sizeof(error);
- if( getsockopt( C->sock, SOL_SOCKET, SO_ERROR, &error, &len ) < 0 )
- return(-1);
- }
- } /* end of connect conditional */
-
-FINISH:
- /**
- * make the socket blocking again.
- */
- if(( mknblock( C->sock, FALSE )) < 0 ){
- perror( "JOEsocket: unable to set socket to non-blocking." );
- return -1;
- }
-
- /* if requested, encrypt the transaction */
- if( C->prot == HTTPS ){
- #ifdef HAVE_SSL
- /* currently a fatal error, should it be? */
- if(( SSL_set_fd( C->ssl, C->sock )) < 0 ){
- perror( "unable to create secure socket!" );
- exit( 0 );
- }
- if(( serr = SSL_connect( C->ssl )) < 0 ){
- int problem = SSL_get_error( C->ssl, serr );
- fprintf( stderr, "SSL_connect: want to %s more...\n",
- ( problem == SSL_ERROR_WANT_READ) ? "read" : "write");
- return -1;
- }
- SSL_get_cipher( C->ssl );
- #else
- return -1;
- #endif /* HAVE_SSL */
- }
-
- return((C->sock>0)?C->sock:-1);
-}
-
-/**
- * makes the socket non-blocking,
- * calls select with timeout and
- * returns the socket to blocking.
- */
-int
-JOEsocket_check( CONN *C, SDSET test )
-{
- int res;
- struct timeval timeout;
-
- FD_ZERO(&C->rset);
- FD_ZERO(&C->wset);
- FD_SET( C->sock, &C->rset );
- FD_SET( C->sock, &C->wset );
- memset((void *)&timeout, '\0', sizeof( struct timeval ));
-
- if(( mknblock( C->sock, TRUE )) < 0 ){
- perror( "SIMBOTsocket: unable to set socket to non-blocking." );
- return -1;
- }
-
- timeout.tv_sec = ( C->timeout > 0)?C->timeout:60;
- timeout.tv_usec = 0;
-
- switch( test ){
- case READ:
- if(( res = select( C->sock+1, &C->rset, NULL, NULL, &timeout )) < 1 ){
- close( C->sock );
- return -1;
- }
- break;
- case WRITE:
- if(( res = select( C->sock+1, NULL, &C->wset, NULL, &timeout )) < 1 ){
- close( C->sock );
- return -1;
- }
- break;
- case RDWR:
- if(( res = select( C->sock+1, &C->rset, &C->wset, NULL, &timeout )) < 1 ){
- close( C->sock );
- return -1;
- }
- break;
- }
-
- if(( mknblock( C->sock, FALSE )) < 0 ){
- perror( "SIMBOTsocket: unable to set socket to non-blocking." );
- return -1;
- }
- FD_CLR( C->sock, &C->rset );
-
- return 0;
-}
-
-/**
- * local function
- * set socket to non-blocking
- */
-int
-mknblock( int sock, int nonblock )
-{
-#if HAVE_FCNTL_H
- int flags;
- int retval;
-
- flags = fcntl( sock, F_GETFL, 0 );
- if( flags < 0 ){
- perror("fcntl");
- return -1;
- }
- if( nonblock ){
- flags |= O_NDELAY;
- }
- else{
- flags &= ~O_NDELAY;
- }
- retval = fcntl( sock, F_SETFL, flags);
- if( retval < 0 ){
- perror("fcntl");
- return -1;
- }
- return retval;
-#elif defined( FIONBIO )
- ioctl_t status;
-
- status = nb ? 1 : 0;
- return ioctl( sd, FIONBIO, &status );
-#else
- return -1;
-#endif
-}
-
-/**
- * local function
- * returns ssize_t
- * writes vbuf to sock
- */
-ssize_t
-socket_write( int sock, const void *vbuf, size_t len )
-{
- size_t n;
- ssize_t w;
- const char *buf;
-
- buf = vbuf;
- n = len;
- while( n > 0 ){
- if(( w = write( sock, buf, n )) <= 0 ){
- if( errno == EINTR )
- w = 0;
- else
- return( -1 );
- }
- n -= w;
- buf += n;
- }
- return( len );
-}
-
-/**
- * local function
- * returns ssize_t
- * writes vbuf to sock
- */
-ssize_t
-ssl_socket_write( CONN *C, const void *vbuf, size_t len )
-{
-#ifdef HAVE_SSL
- size_t n;
- ssize_t w;
- const char *buf;
-
- buf = vbuf;
- n = len;
- while( n > 0 ){
- if(( w = SSL_write( C->ssl, buf, n )) <= 0 ){
- if( errno == EINTR )
- w = 0;
- else
- return( -1 );
- }
- n -= w;
- buf += n;
- }
- return( len );
-#else
- perror( "protocol not supported" );
- return -1;
-#endif/*HAVE_SSL*/
-}
-
-ssize_t
-JOEreadline( CONN *C, char *ptr, size_t len )
-{
- int n;
-
- do{
- n = read( C->sock, ptr, 1 );
- } while( n > 0 && *ptr++ != '\n' );
-
- *ptr++=0;
- return( n > 0 );
-}
-
-ssize_t
-JOEsocket_read( CONN *C, void *vbuf, size_t len )
-{
- size_t n;
- ssize_t r;
- char *buf;
-
- buf = vbuf;
- n = len;
-
- if( C->prot == HTTPS ){
- #ifdef HAVE_SSL
- while( n > 0 ){
- if(( r = SSL_read( C->ssl, buf, n )) < 0 ){
- if( errno == EINTR )
- r = 0;
- else
- return( -1 );
- }
- else if( r == 0 ) break;
- n -= r;
- buf += r;
- } /* end of while */
- #endif/*HAVE_SSL*/
- }
- else{
- while( n > 0 ){
- if(( r = read( C->sock, buf, n )) < 0 ){
- if( errno == EINTR )
- r = 0;
- else
- return( -1 );
- }
- else if( r == 0 ) break;
- n -= r;
- buf += r;
- } /* end of while */
- } /* end of else */
-
- return( len - n );
-}
-
-/**
- * returns void
- * socket_write wrapper function.
- */
-int
-JOEsocket_write( CONN *C, const void *buf, size_t len )
-{
- int bytes;
-
- if( C->prot == HTTPS ){
- /* handle HTTPS protocol */
- #ifdef HAVE_SSL
- if(( bytes = ssl_socket_write( C, buf, len )) != len ){
- perror( "JOEssl_socket_write: ERROR" );
- return -1;
- }
- #else
- perror( "JOEssl_socket_write: protocol NOT supported" );
- return -1;
- #endif/*HAVE_SSL*/
- }
- else{
- /* assume HTTP */
- if(( bytes = socket_write( C->sock, buf, len )) != len ){
- perror( "JOEsocket_write: ERROR" );
- return -1;
- }
- }
-
- return 0;
-}
-
-/**
- * returns void
- * frees ssl resources if using ssl and
- * closes the connection and the socket.
- */
-void
-JOEclose( CONN *C )
-{
- #ifdef HAVE_SSL
- if( C->prot == HTTPS ){
- SSL_shutdown( C->ssl );
- }
- SSL_free( C->ssl );
- SSL_CTX_free( C->ctx );
- #endif/*HAVE_SSL*/
- close( C->sock );
-
- return;
-}
-
-