blob: ae182295d9137866a4b98f9049227b3598cae8c5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ServerSocket.h"
#include "SocketError.h"
#ifdef HAVE_WINSOCK2_H
#include <Winsock2.h>
#include <Ws2tcpip.h>
#include <sys/stat.h>
#define stat _stat
#else
#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <sys/types.h>
#include <assert.h>
#include <string>
using namespace activemq::network;
#ifdef HAVE_WINSOCK2_H
// Static socket initializer needed for winsock
ServerSocket::StaticServerSocketInitializer::StaticServerSocketInitializer () {
socketInitError = NULL;
const WORD version_needed = MAKEWORD(2,2); // lo-order byte: major version
WSAData temp;
if( WSAStartup(version_needed, &temp )){
clear();
socketInitError = new SocketException ( __FILE__, __LINE__,
"winsock.dll was not found");
}
}
ServerSocket::StaticServerSocketInitializer::~StaticServerSocketInitializer () {
clear();
WSACleanup();
}
// Create static instance of the socket initializer.
ServerSocket::StaticServerSocketInitializer
ServerSocket::staticSocketInitializer;
#endif
////////////////////////////////////////////////////////////////////////////////
ServerSocket::ServerSocket()
{
socketHandle = Socket::INVALID_SOCKET_HANDLE;
#if defined(HAVE_WINSOCK2_H)
if( ServerSocket::staticSocketInitializer.getSocketInitError() != NULL ) {
throw *ServerSocket::staticSocketInitializer.getSocketInitError();
}
#endif
}
////////////////////////////////////////////////////////////////////////////////
ServerSocket::~ServerSocket()
{
// No shutdown, just close - dont want blocking destructor.
close();
}
////////////////////////////////////////////////////////////////////////////////
void ServerSocket::bind( const char* host, int port ) throw ( SocketException )
{
bind (host, port, SOMAXCONN);
}
////////////////////////////////////////////////////////////////////////////////
void ServerSocket::bind( const char* host,
int port,
int backlog ) throw ( SocketException )
{
if(isBound()) {
throw SocketException ( __FILE__, __LINE__,
"ServerSocket::bind - Socket already bound" );
}
// Create the socket.
socketHandle = ::socket(AF_INET, SOCK_STREAM, 0 );
if( socketHandle < 0) {
socketHandle = Socket::INVALID_SOCKET_HANDLE;
throw SocketException( __FILE__, __LINE__, SocketError::getErrorString().c_str());
}
// Verify the port value.
if( port <= 0 || port > 65535 ) {
throw SocketException( __FILE__, __LINE__,
"ServerSocket::bind - Port out of range: %d", port );
}
sockaddr_in bind_addr;
bind_addr.sin_family = AF_INET;
bind_addr.sin_port = htons((short)port);
bind_addr.sin_addr.s_addr = 0; // To be set later down...
memset(&bind_addr.sin_zero, 0, sizeof(bind_addr.sin_zero));
int status;
// Resolve name
#if defined(HAVE_STRUCT_ADDRINFO)
::addrinfo hints;
memset(&hints, 0, sizeof(addrinfo));
hints.ai_family = PF_INET;
struct addrinfo *res_ptr = NULL;
status = ::getaddrinfo(host, NULL, &hints, &res_ptr);
if( status != 0 || res_ptr == NULL) {
throw SocketException( __FILE__, __LINE__, SocketError::getErrorString().c_str() );
}
assert(res_ptr->ai_addr->sa_family == AF_INET);
// Porting: On both 32bit and 64 bit systems that we compile to soo far, sin_addr is a 32 bit value, not an unsigned long.
assert(sizeof(((sockaddr_in*)res_ptr->ai_addr)->sin_addr.s_addr) == 4);
bind_addr.sin_addr.s_addr = ((sockaddr_in*)res_ptr->ai_addr)->sin_addr.s_addr;
freeaddrinfo(res_ptr);
#else
struct ::hostent *he = ::gethostbyname(host);
if( he == NULL ) {
throw SocketException( __FILE__, __LINE__, "Failed to resolve hostname" );
}
bind_addr.sin_addr.s_addr = *((in_addr_t *)he->h_addr);
#endif
// Set the socket to reuse the address.
int value = 1;
::setsockopt(socketHandle, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(int) );
status = ::bind(socketHandle,
reinterpret_cast<sockaddr*>(&bind_addr), sizeof( bind_addr ));
if( status < 0 ){
close();
throw SocketException ( __FILE__, __LINE__,
"ServerSocket::bind - %s", SocketError::getErrorString().c_str() );
}
status = ::listen( socketHandle, (int)backlog );
if( status < 0 ) {
close();
throw SocketException( __FILE__, __LINE__, SocketError::getErrorString().c_str() );
}
}
////////////////////////////////////////////////////////////////////////////////
void ServerSocket::close() throw (cms::CMSException){
if( isBound() ) {
#if !defined(HAVE_WINSOCK2_H)
::close( socketHandle );
#else
::closesocket( socketHandle );
#endif
socketHandle = Socket::INVALID_SOCKET_HANDLE;
}
}
////////////////////////////////////////////////////////////////////////////////
bool ServerSocket::isBound() const {
return this->socketHandle != Socket::INVALID_SOCKET_HANDLE;
}
////////////////////////////////////////////////////////////////////////////////
Socket* ServerSocket::accept() throw (SocketException)
{
struct sockaddr_in temp;
#if !defined(HAVE_WINSOCK2_H)
socklen_t temp_len = sizeof( sockaddr_in );
#else
int temp_len = sizeof( sockaddr_in );
#endif
SocketHandle ss_socket_handle = (SocketHandle)0;
// Loop to ignore any signal interruptions that occur during the operation.
do {
ss_socket_handle = ::accept( socketHandle,
reinterpret_cast<struct sockaddr*>(&temp),
&temp_len );
} while( ss_socket_handle < 0 &&
SocketError::getErrorCode() == SocketError::INTERRUPTED );
if( ss_socket_handle < 0 ) {
throw SocketException( __FILE__, __LINE__,
"ServerSocket::accept- %s", SocketError::getErrorString().c_str() );
}
return new TcpSocket( ss_socket_handle );
}