/*!
	\brief It discards N bytes that are currently waiting to be read on the current socket.

	This function is useful in case we receive a message we cannot undestand (e.g.
	wrong version number when receiving a network packet), so that we have to discard all
	data before reading a new message.

	This function will read 'size' bytes from the socket and discard them.
	It defines an internal buffer in which data will be copied; however, in case
	this buffer is not large enough, it will cycle in order to read everything as well.

	\param sock: the connected socket currently opened.

	\param size: number of bytes that have to be discarded.

	\param errbuf: a pointer to an user-allocated buffer that will contain the complete
	error message. This buffer has to be at least 'errbuflen' in length.
	It can be NULL; in this case the error cannot be printed.

	\param errbuflen: length of the buffer that will contains the error. The error message cannot be
	larger than 'errbuflen - 1' because the last char is reserved for the string terminator.

	\return '0' if everything is fine, '-1' if some errors occurred.
	The error message is returned in the 'errbuf' variable.
*/
int sock_discard(SOCKET sock, int size, char *errbuf, int errbuflen)
{
#define TEMP_BUF_SIZE 32768

    char buffer[TEMP_BUF_SIZE];		// network buffer, to be used when the message is discarded

    // A static allocation avoids the need of a 'malloc()' each time we want to discard a message
    // Our feeling is that a buffer if 32KB is enough for most of the application;
    // in case this is not enough, the "while" loop discards the message by calling the
    // sockrecv() several times.
    // We do not want to create a bigger variable because this causes the program to exit on
    // some platforms (e.g. BSD)

    while (size > TEMP_BUF_SIZE)
    {
        if (sock_recv(sock, buffer, TEMP_BUF_SIZE, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1)
            return -1;

        size-= TEMP_BUF_SIZE;
    }

    // If there is still data to be discarded
    // In this case, the data can fit into the temporaty buffer
    if (size)
    {
        if (sock_recv(sock, buffer, size, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1)
            return -1;
    }

    SOCK_ASSERT("I'm currently discarding data\n", 1);

    return 0;
}
Exemplo n.º 2
0
Arquivo: rpcapd.c Projeto: CM44/npcap
void main_cleanup_childs(int sign)
{
pid_t pid;
int stat;

	// For reference, Stevens, pg 128

	while ( (pid= waitpid(-1, &stat, WNOHANG) ) > 0)
		SOCK_ASSERT("Child terminated", 1);

	return;
}
/*!
	\brief It waits on a connected socket and it manages to receive data.

	This function basically calls the recv() socket function and it checks that no
	error occurred. If that happens, it writes the error message into 'errbuf'.

	This function changes its behaviour according to the 'receiveall' flag: if we
	want to receive exactly 'size' byte, it loops on the recv()	until all the requested
	data is arrived. Otherwise, it returns the data currently available.

	In case the socket does not have enough data available, it cycles on the recv()
	util the requested data (of size 'size') is arrived.
	In this case, it blocks until the number of bytes read is equal to 'size'.

	\param sock: the connected socket currently opened.

	\param buffer: a char pointer to a user-allocated buffer in which data has to be stored

	\param size: size of the allocated buffer. WARNING: this indicates the number of bytes
	that we are expecting to be read.

	\param receiveall: if '0' (or SOCK_RECEIVEALL_NO), it returns as soon as some data
	is ready; otherwise, (or SOCK_RECEIVEALL_YES) it waits until 'size' data has been
	received (in case the socket does not have enough data available).

	\param errbuf: a pointer to an user-allocated buffer that will contain the complete
	error message. This buffer has to be at least 'errbuflen' in length.
	It can be NULL; in this case the error cannot be printed.

	\param errbuflen: length of the buffer that will contains the error. The error message cannot be
	larger than 'errbuflen - 1' because the last char is reserved for the string terminator.

	\return the number of bytes read if everything is fine, '-1' if some errors occurred.
	The error message is returned in the 'errbuf' variable.
*/
int sock_recv(SOCKET sock, char *buffer, int size, int receiveall, char *errbuf, int errbuflen)
{
    int nread;
    int totread= 0;
    // We can obtain the same result using the MSG_WAITALL flag
    // However, this is not supported by recv() in Win32

    if (size == 0)
    {
        SOCK_ASSERT("I have been requested to read zero bytes", 1);
        return 0;
    }

again:
    nread= recv(sock, &(buffer[totread]), size - totread, 0);

    if (nread == -1)
    {
        sock_geterror("recv(): ", errbuf, errbuflen);
        return -1;
    }

    if (nread == 0)
    {
        if (errbuf)
        {
            snprintf(errbuf, errbuflen, "The other host terminated the connection.");
            errbuf[errbuflen - 1]= 0;
        }

        return -1;
    }

    // If we want to return as soon as some data has been received,
    // let's do the job
    if (!receiveall)
        return nread;

    totread+= nread;

    if (totread != size)
        goto again;

    return totread;
}
Exemplo n.º 4
0
Arquivo: rpcapd.c Projeto: CM44/npcap
/*
	\brief Closes gracefully (more or less) the program.

	This function is called:
	- when we're running in console
	- when we're running as a Win32 service (in case we press STOP)

	It is not called when we are running as a daemon on UNIX, since
	we do not define a signal in order to terminate gracefully the daemon.

	This function makes a fast cleanup (it does not clean everything, as 
	you can see from the fact that it uses kill() on UNIX), closes
	the main socket, free winsock resources (on Win32) and exits the
	program.
*/
void main_cleanup(int sign)
{
#ifndef WIN32
	// Sends a KILL signal to all the processes
	// that share the same process group (i.e. kills all the childs)
	kill(0, SIGKILL);
#endif

	SOCK_ASSERT(PROGRAM_NAME " is closing.\n", 1);

	// FULVIO (bug)
	// Here we close only the latest 'sockmain' created; if we opened more than one waiting sockets, 
	// only the latest one is closed correctly.
	if (sockmain)
		closesocket(sockmain);
	sock_cleanup();

	/*
		This code is executed under the following conditions:
		- SIGTERM: we're under UNIX, and the user kills us with 'kill -15' 
		(no matter is we're a daemon or in a console mode)
		- SIGINT: we're in console mode and the user sends us a Ctrl+C 
		(SIGINT signal), no matter if we're UNIX or Win32

		In all these cases, we have to terminate the program.
		The case that still remains is if we're a Win32 service: in this case,
		we're a child thread, and we want just to terminate ourself. This is because
		the exit(0) will be invoked by the main thread, which is blocked waiting that
		all childs terminates. We are forced to call exit from the main thread otherwise
		the Win32 service control manager (SCM) does not work well.
	*/
	if ( (sign == SIGTERM) || (sign == SIGINT) )
		exit(0);
	else
		return;
}
Exemplo n.º 5
0
VOID
SockCompleteRequest(
    PSOCKET_INFORMATION SocketInfo,
    DWORD Status,
    DWORD Information,
    LPWSAOVERLAPPED Overlapped,
    LPWSAOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine,
    LPWSATHREADID ThreadId
    )

/*++

Routine Description:

    Completes a socket IO request. This routine checks the state of the
    given socket. If the socket is overlapped, and an overlapped structure
    was provided, then the appropriate completion mechanism is invoked.

Arguments:

    SocketInfo - The socket to complete.

    Status - The completion status.

    Information - Completion information. Usually the number of bytes
        transferred.

    Overlapped - Pointer to an WSAOVERLAPPED structure.

    CompletionRoutine - Optional pointer to a completion routine to
        schedule.

    ThreadId - Identifies the target thread for the completion routine.

Return Value:

    None.

--*/

{

    INT result;
    INT err;
    PSOCK_IO_STATUS ioStatus;

    SOCK_ASSERT( SocketInfo != NULL );
    SOCK_ASSERT( ThreadId != NULL );
    SOCK_ASSERT( Status != WSA_IO_PENDING );

    IF_DEBUG(OVERLAP) {

        SOCK_PRINT((
            "SockCompleteRequest: socket %lx, status %d, info %lu\n",
            SocketInfo,
            Status,
            Information
            ));

    }

    //
    // Determine if we need to even bother with this stuff.
    //

    if( ( SocketInfo->CreationFlags & WSA_FLAG_OVERLAPPED ) == 0 ||
        Overlapped == NULL ) {

        return;

    }

    //
    // OK, we've got an overlapped socket and an overlapped structure.
    // Update the info in the overlapped structure.
    //

    ioStatus = SOCK_OVERLAPPED_TO_IO_STATUS(Overlapped);

    ioStatus->Status = Status;
    ioStatus->Information = Information;

    //
    // If we've got a completion routine, schedule it. Otherwise, if the
    // overlapped structure has an event handle, signal it.
    //

    if( CompletionRoutine != NULL ) {

        //
        // On 64-bit system both Offset and OffsetHigh fields are used to store
        // the pointer to the completion routine
        //
#ifdef _WIN64
        *((LPVOID *)(&Overlapped->Offset)) = CompletionRoutine;
#else
        Overlapped->Offset = (DWORD)CompletionRoutine;
#endif

        result = SockUpcallTable.lpWPUQueueApc(
                     ThreadId,
                     &SockUserApc,
                     (ULONG_PTR)Overlapped,
                     &err
                     );

        if( result != NO_ERROR ) {

            SOCK_PRINT((
                "SockCompleteRequest: WPUQueueApc failed, error %d\n",
                err
                ));

        }

    } else {

        if( Overlapped->hEvent != NULL ) {

            SetEvent( Overlapped->hEvent );

        }

    }

}   // SockCompleteRequest
Exemplo n.º 6
0
VOID
CALLBACK
SockUserApc(
    ULONG_PTR Context
    )

/*++

Routine Description:

    Private APC completion routine. This routine unpacks the necessary
    parameters, then invokes the user's completion routine.

Arguments:

    Context - Actually a pointer to the request's WSAOVERLAPPED structure.

Return Value:

    None.

--*/

{

    LPWSAOVERLAPPED Overlapped;
    LPWSAOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine;
    PSOCK_IO_STATUS ioStatus;

    //
    // Retrieve a pointer to the WSAOVERLAPPED structure, extract the
    // pointer to the completion routine, and construct a pointer to
    // the SOCK_IO_STATUS block.
    //

    Overlapped = (LPWSAOVERLAPPED)Context;
    SOCK_ASSERT( Overlapped != NULL );

    //
    // On 64-bit system both Offset and OffsetHigh fields are used to store the pointer
    // to the completion routine
    //
#ifdef _WIN64
    CompletionRoutine = *((LPWSAOVERLAPPED_COMPLETION_ROUTINE *)(&Overlapped->Offset));
#else
    CompletionRoutine = (LPWSAOVERLAPPED_COMPLETION_ROUTINE)Overlapped->Offset;
#endif

    SOCK_ASSERT( CompletionRoutine != NULL );

    ioStatus = SOCK_OVERLAPPED_TO_IO_STATUS(Overlapped);

    //
    // Invoke the user's completion routine.
    //

    (CompletionRoutine)(
        ioStatus->Status,
        ioStatus->Information,
        Overlapped,
        0
        );

}   // SockUserApc
Exemplo n.º 7
0
BOOL
SockInitializeOverlappedThread(
    PSOCKET_INFORMATION SocketInfo,
    PSOCK_OVERLAPPED_DATA OverlappedData,
    LPTHREAD_START_ROUTINE ThreadStartAddress
    )

/*++

Routine Description:

    Initializes the SOCK_OVERLAPPED_DATA for the given socket, starting
    the worker thread if necessary.

    N.B. This MUST be called with the socket lock held!

Arguments:

    SocketInfo - The socket.

    OverlappedData - Points to other SocketInfo->OverlappedRecv or
        SocketInfo->OverlappedSend.

    ThreadStartAddress - Points to the worker thread.

Return Value:

    BOOL - TRUE if everything initialized successfully, FALSE otherwise.

--*/

{

    HANDLE threadHandle;
    DWORD threadId;

    //
    // Sanity check.
    //

    SOCK_ASSERT( SocketInfo != NULL );
    SOCK_ASSERT( OverlappedData != NULL );
    SOCK_ASSERT( ThreadStartAddress != NULL );
    SOCK_ASSERT( OverlappedData == &SocketInfo->OverlappedRecv ||
                 OverlappedData == &SocketInfo->OverlappedSend );

    //
    // Bail if everything is already initialized.
    //

    if( OverlappedData->WakeupEvent != NULL ) {

        return TRUE;

    }

    //
    // Create the event object.
    //

    OverlappedData->WakeupEvent = CreateEvent(
                                      NULL,
                                      FALSE,
                                      FALSE,
                                      NULL
                                      );

    if( OverlappedData->WakeupEvent == NULL ) {

        return FALSE;

    }

    //
    // Add a new reference to the socket. The worker thread
    // will remove this reference before the thread exits.
    //

    SockReferenceSocket( SocketInfo );

    //
    // Create the worker thread.
    //

    threadHandle = SockCreateWorkerThread(
                       NULL,
                       0,
                       ThreadStartAddress,
                       (LPVOID)SocketInfo,
                       0,
                       &threadId
                       );

    if( threadHandle == NULL ) {

        CloseHandle( OverlappedData->WakeupEvent );
        OverlappedData->WakeupEvent = NULL;

        SockDereferenceSocket( SocketInfo );
        return FALSE;

    }

    return TRUE;

}   // SockInitializeOverlappedThread
Exemplo n.º 8
0
/*!
	\brief Main serving funtion
	This function is the one which does the job. It is the main() of the child
	thread, which is created as soon as a new connection is accepted.

	\param ptr: a void pointer that keeps the reference of the 'pthread_chain'
	value corrisponding to this thread. This variable is casted into a 'pthread_chain'
	value in order to retrieve the socket we're currently using, the therad ID, and 
	some pointers to the previous and next elements into this struct.

	\return None.
*/
void daemon_serviceloop( void *ptr )
{
char errbuf[PCAP_ERRBUF_SIZE + 1];		// keeps the error string, prior to be printed
char source[PCAP_BUF_SIZE];				// keeps the string that contains the interface to open
struct rpcap_header header;				// RPCAP message general header
pcap_t *fp= NULL;						// pcap_t main variable
struct daemon_slpars *pars;				// parameters related to the present daemon loop

pthread_t threaddata= 0;				// handle to the 'read from daemon and send to client' thread

unsigned int ifdrops, ifrecv, krnldrop, svrcapt;	// needed to save the values of the statistics

struct rpcap_sampling samp_param;		// in case sampling has been requested

// Structures needed for the select() call
fd_set rfds;						// set of socket descriptors we have to check
struct timeval tv;					// maximum time the select() can block waiting for data
int retval;							// select() return value


	pars= (struct daemon_slpars *) ptr;
	
	*errbuf= 0;	// Initialize errbuf

	// If we're in active mode, this is not a separate thread
	if (! pars->isactive)
	{
		// Modify thread params so that it can be killed at any time
		if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) )
			goto end;
		if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL) )
			goto end;
	}

auth_again:
	// If we're in active mode, we have to check for the initial timeout
	if (!pars->isactive)
	{
		FD_ZERO(&rfds);
		// We do not have to block here
		tv.tv_sec = RPCAP_TIMEOUT_INIT;
		tv.tv_usec = 0;
		
		FD_SET(pars->sockctrl, &rfds);

		retval = select(pars->sockctrl + 1, &rfds, NULL, NULL, &tv);
		if (retval == -1)
		{
			sock_geterror("select(): ", errbuf, PCAP_ERRBUF_SIZE);
			rpcap_senderror(pars->sockctrl, errbuf, PCAP_ERR_NETW, NULL);
			goto end;
		}

		// The timeout has expired
		// So, this was a fake connection. Drop it down
		if (retval == 0)
		{
			rpcap_senderror(pars->sockctrl, "The RPCAP initial timeout has expired", PCAP_ERR_INITTIMEOUT, NULL);
			goto end;
		}
	}


	retval= daemon_checkauth(pars->sockctrl, pars->nullAuthAllowed, errbuf);

	if (retval)
	{
		// the other user requested to close the connection
		// It can be also the case of 'active mode', in which this host is not
		// allowed to connect to the other peer; in that case, it drops down the connection
		if (retval == -3) 
			goto end;

		// It can be an authentication failure or an unrecoverable error
		rpcap_senderror(pars->sockctrl, errbuf, PCAP_ERR_AUTH, NULL);

		// authentication error
		if (retval == -2)
		{
			// suspend for 1 sec
			// WARNING: this day is inserted only in this point; if the user drops down the connection
			// and it connects again, this suspension time does not have any effects.
			pthread_suspend(RPCAP_SUSPEND_WRONGAUTH*1000);
			goto auth_again;
		}

		 // Unrecoverable error
		if (retval == -1)
			goto end;
	}

	while (1)
	{
	int retval;

		errbuf[0]= 0;	// clear errbuf

		// Avoid zombies connections; check if the connection is opens but no commands are performed
		// from more than RPCAP_TIMEOUT_RUNTIME
		// Conditions:
		// - I have to be in normal mode (no active mode)
		// - if the device is open, I don't have to be in the middle of a capture (fp->rmt_sockdata)
		// - if the device is closed, I have always to check if a new command arrives
		//
		// Be carefully: the capture can have been started, but an error occurred (so fp != NULL, but
		//  rmt_sockdata is 0
		if ( (!pars->isactive) &&  ( (fp == NULL) || ( (fp != NULL) && (fp->rmt_sockdata == 0) ) ))
		{
			// Check for the initial timeout
			FD_ZERO(&rfds);
			// We do not have to block here
			tv.tv_sec = RPCAP_TIMEOUT_RUNTIME;
			tv.tv_usec = 0;
			
			FD_SET(pars->sockctrl, &rfds);

			retval = select(pars->sockctrl + 1, &rfds, NULL, NULL, &tv);
			if (retval == -1)
			{
				sock_geterror("select(): ", errbuf, PCAP_ERRBUF_SIZE);
				rpcap_senderror(pars->sockctrl, errbuf, PCAP_ERR_NETW, NULL);
				goto end;
			}

			// The timeout has expired
			// So, this was a fake connection. Drop it down
			if (retval == 0)
			{
				SOCK_ASSERT("The RPCAP runtime timeout has expired", 1);
				rpcap_senderror(pars->sockctrl, "The RPCAP runtime timeout has expired", PCAP_ERR_RUNTIMETIMEOUT, NULL);
				goto end;
			}
		}

		if (sock_recv(pars->sockctrl, (char *) &header, sizeof(struct rpcap_header), SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE) == -1)
			goto end;

		// Checks if the message is correct
		// In case it is wrong, it discard the data
		retval= rpcap_checkmsg(errbuf, pars->sockctrl, &header,
			RPCAP_MSG_FINDALLIF_REQ,
			RPCAP_MSG_OPEN_REQ,
			RPCAP_MSG_STARTCAP_REQ,
			RPCAP_MSG_UPDATEFILTER_REQ,
			RPCAP_MSG_STATS_REQ,
			RPCAP_MSG_ENDCAP_REQ,
			RPCAP_MSG_SETSAMPLING_REQ,
			RPCAP_MSG_CLOSE,
			RPCAP_MSG_ERROR,
			0);

		switch (retval)
		{
			case -3:	// Unrecoverable network error
				goto end;	// Do nothing; just exit from findalldevs; the error code is already into the errbuf

			case -2:	// The other endpoint send a message that is not allowed here
			{
				rpcap_senderror(pars->sockctrl, "The RPCAP daemon received a message that is not valid", PCAP_ERR_WRONGMSG, errbuf);
			}
			case -1:	// The other endpoint has a version number that is not compatible with our
			{
				rpcap_senderror(pars->sockctrl, "RPCAP version number mismatch", PCAP_ERR_WRONGVER, errbuf);
			}
			break;

			case RPCAP_MSG_FINDALLIF_REQ:
			{
				// Checks that the header does not contain other data; if so, discard it
				if (ntohl(header.plen))
					sock_discard(pars->sockctrl, ntohl(header.plen), errbuf, PCAP_ERRBUF_SIZE);

				if (daemon_findalldevs(pars->sockctrl, errbuf) )
					SOCK_ASSERT(errbuf, 1);

				break;
			};

			case RPCAP_MSG_OPEN_REQ:
			{
				retval= daemon_opensource(pars->sockctrl, source, sizeof(source), ntohl(header.plen), errbuf);

				if (retval == -1)
					SOCK_ASSERT(errbuf, 1);

				break;
			};

			case RPCAP_MSG_SETSAMPLING_REQ:
			{
				retval= daemon_setsampling(pars->sockctrl, &samp_param, ntohl(header.plen), errbuf);

				if (retval == -1)
					SOCK_ASSERT(errbuf, 1);

				break;
			};

			case RPCAP_MSG_STARTCAP_REQ:
			{
				fp= daemon_startcapture(pars->sockctrl, &threaddata, source, pars->isactive, &samp_param, ntohl(header.plen), errbuf);

				if (fp == NULL)
					SOCK_ASSERT(errbuf, 1);

				break;
			};

			case RPCAP_MSG_UPDATEFILTER_REQ:
			{
				if (fp)
				{
					if (daemon_updatefilter(fp, ntohl(header.plen)) )
						SOCK_ASSERT(fp->errbuf, 1);
				}
				else
				{
					rpcap_senderror(pars->sockctrl, "Device not opened. Cannot update filter", PCAP_ERR_UPDATEFILTER, errbuf);
				}

				break;
			};

			case RPCAP_MSG_STATS_REQ:
			{
				// Checks that the header does not contain other data; if so, discard it
				if (ntohl(header.plen))
					sock_discard(pars->sockctrl, ntohl(header.plen), errbuf, PCAP_ERRBUF_SIZE);

				if (fp)
				{
					if (daemon_getstats(fp) )
						SOCK_ASSERT(fp->errbuf, 1);
				}
				else
				{
					SOCK_ASSERT("GetStats: this call should't be allowed here", 1);

					if (daemon_getstatsnopcap(pars->sockctrl, ifdrops, ifrecv, krnldrop, svrcapt, errbuf) )
						SOCK_ASSERT(errbuf, 1);
					// we have to keep compatibility with old applications, which ask for statistics
					// also when the capture has already stopped

//					rpcap_senderror(pars->sockctrl, "Device not opened. Cannot get statistics", PCAP_ERR_GETSTATS, errbuf);
				}

				break;
			};

			case RPCAP_MSG_ENDCAP_REQ:		// The other endpoint close the current capture session
			{
				if (fp)
				{
				struct pcap_stat stats;

					// Save statistics (we can need them in the future)
					if (pcap_stats(fp, &stats) )
					{
						ifdrops= stats.ps_ifdrop;
						ifrecv= stats.ps_recv;
						krnldrop= stats.ps_drop;
						svrcapt= fp->md.TotCapt;
					}
					else
						ifdrops= ifrecv= krnldrop= svrcapt= 0;

					if ( daemon_endcapture(fp, &threaddata, errbuf) )
						SOCK_ASSERT(errbuf, 1);
					fp= NULL;
				}
				else
				{
					rpcap_senderror(pars->sockctrl, "Device not opened. Cannot close the capture", PCAP_ERR_ENDCAPTURE, errbuf);
				}
				break;
			};

			case RPCAP_MSG_CLOSE:		// The other endpoint close the pcap session
			{
				// signal to the main that the user closed the control connection
				// This is used only in case of active mode
				pars->activeclose= 1;	
				SOCK_ASSERT("The other end system asked to close the connection.", 1);
				goto end;
				break;
			};

			case RPCAP_MSG_ERROR:		// The other endpoint reported an error
			{
				// Do nothing; just exit; the error code is already into the errbuf
				SOCK_ASSERT(errbuf, 1);
				break;
			};

			default:
			{
				SOCK_ASSERT("Internal error.", 1);
				break;
			};
		}
	}

end:
	// The child thread is about to end

	// perform pcap_t cleanup, in case it has not been done
	if (fp)
	{
		if (threaddata)
		{
			pthread_cancel(threaddata);
			threaddata= 0;
		}
		if (fp->rmt_sockdata)
		{
			sock_close(fp->rmt_sockdata, NULL, 0);
			fp->rmt_sockdata= 0;
		}
		pcap_close(fp);
		fp= NULL;
	}

	// Print message and exit
	SOCK_ASSERT("I'm exiting from the child loop", 1);
	SOCK_ASSERT(errbuf, 1);

	if (!pars->isactive)
	{
		if (pars->sockctrl)
			sock_close(pars->sockctrl, NULL, 0);
		
		free(pars);
#ifdef WIN32
		pthread_exit(0);
#endif
	}
}
Exemplo n.º 9
0
/*!
	\brief It checks if the authentication credentials supplied by the user are valid.

	This function is called each time the rpcap daemon starts a new serving thread.
	It reads the authentication message from the network and it checks that the 
	user information are valid.

	\param sockctrl: the socket if of the control connection.
	
	\param nullAuthAllowed: '1' if the NULL authentication is allowed.

	\param errbuf: a user-allocated buffer in which the error message (if one) has to be written.

	\return '0' if everything is fine, '-1' if an unrecoverable error occurred.
	The error message is returned in the 'errbuf' variable.
	'-2' is returned in case the authentication failed or in case of a recoverable error (like
	wrong version). In that case, 'errbuf' keeps the reason of the failure. This provides
	a way to know that the connection does not have to be closed.

	In case the message is a 'CLOSE' or an 'ERROR', it returns -3. The error can be due to a
	connection refusal in active mode, since this host cannot be allowed to connect to the remote
	peer.
*/
int daemon_checkauth(SOCKET sockctrl, int nullAuthAllowed, char *errbuf)
{
struct rpcap_header header;			// RPCAP message general header
int retval;							// generic return value
unsigned int nread;					// number of bytes of the payload read from the socket
struct rpcap_auth auth;				// RPCAP authentication header
char *string1, *string2;			// two strings exchanged by the authentication message
unsigned int plen;					// length of the payload
int retcode;						// the value we have to return to the caller

	if (sock_recv(sockctrl, (char *) &header, sizeof(struct rpcap_header), SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE) == -1)
		return -1;

	plen= ntohl(header.plen);

	retval= rpcap_checkmsg(errbuf, sockctrl, &header,
		RPCAP_MSG_AUTH_REQ,
		RPCAP_MSG_CLOSE,
		0);

	if (retval != RPCAP_MSG_AUTH_REQ)
	{
		switch (retval)
		{
			case -3:	// Unrecoverable network error
				return -1;	// Do nothing; just exit; the error code is already into the errbuf

			case -2:	// The other endpoint send a message that is not allowed here
			case -1:	// The other endpoint has a version number that is not compatible with our
				return -2;

			case RPCAP_MSG_CLOSE:
			{
				// Check if all the data has been read; if not, discard the data in excess
				if (ntohl(header.plen) )
				{
					if (sock_discard(sockctrl, ntohl(header.plen), NULL, 0) )
					{
						retcode= -1;
						goto error;
					}
				}		
				return -3;
			};

			case RPCAP_MSG_ERROR:
				return -3;

			default:
			{
				SOCK_ASSERT("Internal error.", 1);
				retcode= -2;
				goto error;
			};
		}
	}

	// If it comes here, it means that we have an authentication request message
	if ( (nread= sock_recv(sockctrl, (char *) &auth, sizeof(struct rpcap_auth), SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE)) == -1)
	{
		retcode= -1;
		goto error;
	}

	switch (ntohs(auth.type) )
	{
		case RPCAP_RMTAUTH_NULL:
		{
			if (!nullAuthAllowed)
			{
				snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed; NULL autentication not permitted.");
				retcode= -2;
				goto error;
			}
			break;
		}

		case RPCAP_RMTAUTH_PWD:
		{
		int len1, len2;

			len1= ntohs(auth.slen1);
			len2= ntohs(auth.slen2);

			string1= (char *) malloc (len1 + 1);
			string2= (char *) malloc (len2 + 1);

			if ( (string1 == NULL) || (string2 == NULL) )
			{
				snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno));
				retcode= -1;
				goto error;
			}

			if ( (nread+= sock_recv(sockctrl, string1, len1, SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE)) == -1)
			{
				retcode= -1;
				goto error;
			}
			if ( (nread+= sock_recv(sockctrl, string2, len2, SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE)) == -1)
			{
				retcode= -1;
				goto error;
			}

			string1[len1]= 0;
			string2[len2]= 0;

			if (daemon_AuthUserPwd(string1, string2, errbuf) )
			{
				retcode= -2;
				goto error;
			}

			break;
			}

		default:
			snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication type not recognized.");
			retcode= -2;
			goto error;
	}


	// Check if all the data has been read; if not, discard the data in excess
	if (nread != plen)
	{
		if (sock_discard(sockctrl, plen - nread, NULL, 0) )
		{
			retcode= -1;
			goto error;
		}
	}

	rpcap_createhdr(&header, RPCAP_MSG_AUTH_REPLY, 0, 0);

	// Send the ok message back
	if ( sock_send(sockctrl, (char *) &header, sizeof (struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1)
	{
		retcode= -1;
		goto error;
	}

	return 0;

error:
	// Check if all the data has been read; if not, discard the data in excess
	if (nread != plen)
		sock_discard(sockctrl, plen - nread, NULL, 0);

	return retcode;
}
Exemplo n.º 10
0
void *daemon_thrdatamain(void *ptr)
{
char errbuf[PCAP_ERRBUF_SIZE + 1];	// error buffer
pcap_t *fp;							// pointer to a 'pcap' structure
int retval;							// general variable used to keep the return value of other functions
struct rpcap_pkthdr *net_pkt_header;// header of the packet
struct pcap_pkthdr *pkt_header;		// pointer to the buffer that contains the header of the current packet
u_char *pkt_data;					// pointer to the buffer that contains the current packet
char *sendbuf;						// temporary buffer in which data to be sent is buffered
int sendbufidx;						// index which keeps the number of bytes currently buffered

	fp= (pcap_t *) ptr;

	fp->md.TotCapt= 0;			// counter which is incremented each time a packet is received

	// Initialize errbuf
	memset(errbuf, 0, sizeof(errbuf) );

	// Some platforms (e.g. Win32) allow creating a static variable with this size
	// However, others (e.g. BSD) do not, so we're forced to allocate this buffer dynamically
	sendbuf= (char *) malloc (sizeof(char) * RPCAP_NETBUF_SIZE);
	if (sendbuf == NULL)
	{
		snprintf(errbuf, sizeof(errbuf) - 1, "Unable to create the buffer for this child thread");
		goto error;
	}

	// Modify thread params so that it can be killed at any time
	if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) )
		goto error;
	if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL) )
		goto error;

	// Retrieve the packets
	while ((retval = pcap_next_ex(fp, &pkt_header, (const u_char **) &pkt_data)) >= 0)	// cast to avoid a compiler warning
	{
		if (retval == 0)	// Read timeout elapsed
			continue;

		sendbufidx= 0;

		// Bufferize the general header
		if ( sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, &sendbufidx,
			RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE) == -1)
			goto error;

		rpcap_createhdr( (struct rpcap_header *) sendbuf, RPCAP_MSG_PACKET, 0,
			(uint16) (sizeof(struct rpcap_pkthdr) + pkt_header->caplen) );

		net_pkt_header= (struct rpcap_pkthdr *) &sendbuf[sendbufidx];

		// Bufferize the pkt header
		if ( sock_bufferize(NULL, sizeof(struct rpcap_pkthdr), NULL, &sendbufidx,
			RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE) == -1)
			goto error;

		net_pkt_header->caplen= htonl(pkt_header->caplen);
		net_pkt_header->len= htonl(pkt_header->len);
		net_pkt_header->npkt= htonl( ++(fp->md.TotCapt) );
		net_pkt_header->timestamp_sec= htonl(pkt_header->ts.tv_sec);
		net_pkt_header->timestamp_usec= htonl(pkt_header->ts.tv_usec);

		// Bufferize the pkt data
		if ( sock_bufferize((char *) pkt_data, pkt_header->caplen, sendbuf, &sendbufidx,
			RPCAP_NETBUF_SIZE, SOCKBUF_BUFFERIZE, errbuf, PCAP_ERRBUF_SIZE) == -1)
			goto error;

		// Send the packet
		if ( sock_send(fp->rmt_sockdata, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1)
			goto error;

	}

	if (retval == -1)
	{
		snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error reading the packets: %s", pcap_geterr(fp) );
		rpcap_senderror(fp->rmt_sockctrl, errbuf, PCAP_ERR_READEX, NULL);
		goto error;
	}

error:

	SOCK_ASSERT(errbuf, 1);
 	closesocket(fp->rmt_sockdata);
	fp->rmt_sockdata= 0;

	free(sendbuf);

	return NULL;
}
Exemplo n.º 11
0
INT
WSPAPI
WSPAddressToString(
    IN LPSOCKADDR lpsaAddress,
    IN DWORD dwAddressLength,
    IN LPWSAPROTOCOL_INFOW lpProtocolInfo,
    OUT LPWSTR lpszAddressString,
    IN OUT LPDWORD lpdwAddressStringLength,
    OUT LPINT lpErrno
    )

/*++

Routine Description:

    This routine converts all components of a SOCKADDR structure into a human-
    readable string representation of the address. This is used mainly for
    display purposes.

Arguments:

    lpsaAddress - Points to a SOCKADDR structure to translate into a string.

    dwAddressLength - The length of the Address SOCKADDR.

    lpProtocolInfo - The WSAPROTOCOL_INFOW struct for a particular provider.

    lpszAddressString - A buffer which receives the human-readable address
        string.

    lpdwAddressStringLength - The length of the AddressString buffer. Returns
        the length of the string actually copied into the buffer.

    lpErrno - A pointer to the error code.

Return Value:

    If no error occurs, WSPAddressToString() returns 0. Otherwise, it returns
        SOCKET_ERROR, and a specific error code is available in lpErrno.

--*/

{

    INT err;
    INT length;
    INT result;
    LPSOCKADDR_IN addr;
    CHAR ansiString[sizeof("aaa.aaa.aaa.aaa:ppppp")];

    SOCK_ENTER( "WSPAddressToString", lpsaAddress, (PVOID)dwAddressLength, lpProtocolInfo, lpszAddressString );

    SOCK_ASSERT( lpErrno != NULL );

    err = SockEnterApi( TRUE, FALSE );

    if( err != NO_ERROR ) {

        SOCK_EXIT( "WSPAddressToString", SOCKET_ERROR, TRUE );
        *lpErrno = err;
        return SOCKET_ERROR;

    }

    //
    // Quick sanity check.
    //

    if( lpsaAddress == NULL ||
        lpszAddressString == NULL ||
        lpdwAddressStringLength == NULL ) {

        err = WSAEFAULT;
        goto exit;

    }

    if( lpsaAddress->sa_family != AF_INET ) {

        err = WSA_INVALID_PARAMETER;
        goto exit;

    }

    //
    // Convert the address to string locally.
    //

    addr = (LPSOCKADDR_IN)lpsaAddress;

    length = wsprintf(
                 ansiString,
                 "%d.%d.%d.%d:%u",
                 ( addr->sin_addr.s_addr >>  0 ) && 0xFF,
                 ( addr->sin_addr.s_addr >>  8 ) && 0xFF,
                 ( addr->sin_addr.s_addr >> 16 ) && 0xFF,
                 ( addr->sin_addr.s_addr >> 24 ) && 0xFF
                 );

    if( addr->sin_port != 0 ) {

        length =+ wsprintf(
                      ansiString + length,
                      ":%u",
                      addr->sin_port
                      );

    }

    SOCK_ASSERT( length < sizeof(ansiString) );

    //
    // Map it to UNICODE.
    //

    result = MultiByteToWideChar(
                 CP_ACP,
                 0,
                 ansiString,
                 -1,
                 lpszAddressString,
                 (INT)*lpdwAddressStringLength
                 );

    if( result == 0 ) {

        err = WSAEFAULT;
        goto exit;

    }

    //
    // Success!
    //

    SOCK_ASSERT( err == NO_ERROR );

    *lpdwAddressStringLength = (DWORD)result;
    result = 0;

exit:

    if( err != NO_ERROR ) {

        *lpErrno = err;
        result = SOCKET_ERROR;

    }

    SOCK_EXIT( "WSPAddressToString", result, (BOOL)( result == SOCKET_ERROR ) );
    return result;

}   // WSPAddressToString
Exemplo n.º 12
0
int sock_recv(SOCKET sock, void *buffer, size_t size, int flags,
    char *errbuf, int errbuflen)
{
	char *bufp = buffer;
	int remaining;
	ssize_t nread;

	if (size == 0)
	{
		SOCK_ASSERT("I have been requested to read zero bytes", 1);
		return 0;
	}
	if (size > INT_MAX)
	{
		if (errbuf)
		{
			pcap_snprintf(errbuf, errbuflen,
			    "Can't read more than %u bytes with sock_recv",
			    INT_MAX);
		}
		return -1;
	}

	bufp = (char *) buffer;
	remaining = (int) size;

	/*
	 * We don't use MSG_WAITALL because it's not supported in
	 * Win32.
	 */
	for (;;) {
		nread = recv(sock, bufp, remaining, 0);

		if (nread == -1)
		{
#ifndef _WIN32
			if (errno == EINTR)
				return -3;
#endif
			sock_geterror("recv(): ", errbuf, errbuflen);
			return -1;
		}

		if (nread == 0)
		{
			if ((flags & SOCK_EOF_IS_ERROR) ||
			    (remaining != (int) size))
			{
				/*
				 * Either we've already read some data,
				 * or we're always supposed to return
				 * an error on EOF.
				 */
				if (errbuf)
				{
					pcap_snprintf(errbuf, errbuflen,
					    "The other host terminated the connection.");
				}
				return -1;
			}
			else
				return 0;
		}

		/*
		 * Do we want to read the amount requested, or just return
		 * what we got?
		 */
		if (!(flags & SOCK_RECEIVEALL_YES))
		{
			/*
			 * Just return what we got.
			 */
			return (int) nread;
		}

		bufp += nread;
		remaining -= nread;

		if (remaining == 0)
			return (int) size;
	}
}
Exemplo n.º 13
0
void fileconf_read(int sign)
{
FILE *fp;
char msg[PCAP_ERRBUF_SIZE + 1];
int i;

#ifndef WIN32
	signal(SIGHUP, fileconf_read);
#endif

	if ((fp= fopen(loadfile, "r") ) != NULL)
	{
	char line[MAX_LINE + 1];
	char *ptr;

		hostlist[0]= 0;
		i= 0;

		while ( fgets(line, MAX_LINE, fp) != NULL )
		{
			if (line[0] == '\n') continue;	// Blank line
			if (line[0] == '\r') continue;	// Blank line
			if (line[0] == '#') continue;	// Comment

			if ( (ptr= strstr(line, "ActiveClient")) )
			{
			char *address, *port;

				ptr= strchr(ptr, '=') + 1;
				address= strtok(ptr, RPCAP_HOSTLIST_SEP);

				if ( (address != NULL) && (i < MAX_ACTIVE_LIST) )
				{
					port = strtok(NULL, RPCAP_HOSTLIST_SEP);
					snprintf(activelist[i].address, MAX_LINE, address);

					if (strcmp(port, "DEFAULT") == 0) // the user choose a custom port
						snprintf(activelist[i].port, MAX_LINE, RPCAP_DEFAULT_NETPORT_ACTIVE);
					else
						snprintf(activelist[i].port, MAX_LINE, port);

					activelist[i].address[MAX_LINE] = 0;
					activelist[i].port[MAX_LINE] = 0;
				}
				else
					SOCK_ASSERT("Only MAX_ACTIVE_LIST active connections are currently supported.", 1);

				i++;
				continue;
			}

			if ( (ptr= strstr(line, "PassiveClient")) )
			{
				ptr= strchr(ptr, '=') + 1;
				strncat(hostlist, ptr, MAX_HOST_LIST);
				strncat(hostlist, ",", MAX_HOST_LIST);
				continue;
			}

			if ( (ptr= strstr(line, "NullAuthPermit")) )
			{
				ptr= strstr(ptr, "YES");
				if (ptr)
					nullAuthAllowed= 1;
				else
					nullAuthAllowed= 0;
				continue;
			}
		}

		// clear the remaining fields of the active list 
		while (i < MAX_ACTIVE_LIST)
		{
			activelist[i].address[0] = 0;
			activelist[i].port[0] = 0;
			i++;
		}

		// Remove all '\n' and '\r' from the strings
		strrem(hostlist, '\r');
		strrem(hostlist, '\n');

		snprintf(msg, PCAP_ERRBUF_SIZE, "New passive host list: %s\n\n", hostlist);
		SOCK_ASSERT(msg, 1);
		fclose(fp);
	}
}
Exemplo n.º 14
0
Arquivo: rpcapd.c Projeto: CM44/npcap
void main_startup(void)
{
char errbuf[PCAP_ERRBUF_SIZE + 1];	// keeps the error string, prior to be printed
struct addrinfo *addrinfo;				// keeps the addrinfo chain; required to open a new socket
int i;
#ifdef WIN32
	pthread_t threadId;					// Pthread variable that keeps the thread structures
	pthread_attr_t detachedAttribute;	// PThread attribute needed to create the thread as detached
#else
	pid_t pid;
#endif

	i= 0;
	addrinfo= NULL;
	memset(errbuf, 0, sizeof(errbuf) );

	// Starts all the active threads
	while ( (activelist[i].address[0] != 0) && (i < MAX_ACTIVE_LIST) )
	{
		activelist[i].ai_family= mainhints.ai_family;
		
#ifdef WIN32
		/* GV we need this to create the thread as detached. */
		/* GV otherwise, the thread handle is not destroyed  */
		pthread_attr_init(&detachedAttribute); 
		pthread_attr_setdetachstate(&detachedAttribute, PTHREAD_CREATE_DETACHED);

		if ( pthread_create( &threadId, &detachedAttribute, (void *) &main_active, (void *) &activelist[i]) )
		{
			SOCK_ASSERT("Error creating the active child thread", 1);
			pthread_attr_destroy(&detachedAttribute);
			continue;
		}
		pthread_attr_destroy(&detachedAttribute);
#else
		if ( (pid= fork() ) == 0)	// I am the child
		{
			main_active( (void *) &activelist[i]);
			exit(0);
		}
#endif
		i++;
	}

	/*
		The code that manages the active connections is not blocking; 
		vice versa, the code that manages the passive connection is blocking.
		So, if the user do not want to run in passive mode, we have to block
		the main thread here, otherwise the program ends and all threads
		are stopped.

		WARNING: this means that in case we have only active mode, the program does
		not terminate even if all the child thread terminates. The user has always to
		press Ctrl+C (or send a SIGTERM) to terminate the program.
	*/

	if (passivemode)
	{
	struct addrinfo *tempaddrinfo;

		// Do the work
		if (sock_initaddress((address[0]) ? address : NULL, port, &mainhints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1)
		{
			SOCK_ASSERT(errbuf, 1);
			return;
		}

		tempaddrinfo= addrinfo;

		while (tempaddrinfo)
		{
		SOCKET *socktemp;

			if ( (sockmain= sock_open(tempaddrinfo, SOCKOPEN_SERVER, SOCKET_MAXCONN, errbuf, PCAP_ERRBUF_SIZE)) == -1)
			{
				SOCK_ASSERT(errbuf, 1);
				tempaddrinfo= tempaddrinfo->ai_next;
				continue;
			}

			// This trick is needed in order to allow the child thread to save the 'sockmain' variable
			// withouth getting it overwritten by the sock_open, in case we want to open more than one waiting sockets
			// For instance, the pthread_create() will accept the socktemp variable, and it will deallocate immediately that variable
			socktemp= (SOCKET *) malloc (sizeof (SOCKET));
			if (socktemp == NULL)
				exit(0);

			*socktemp= sockmain;

#ifdef WIN32
			/* GV we need this to create the thread as detached. */
			/* GV otherwise, the thread handle is not destroyed  */
			pthread_attr_init(&detachedAttribute); 
			pthread_attr_setdetachstate(&detachedAttribute, PTHREAD_CREATE_DETACHED);

			if ( pthread_create( &threadId, &detachedAttribute, (void *) &main_passive, (void *) socktemp ) )
			{
				SOCK_ASSERT("Error creating the passive child thread", 1);
				pthread_attr_destroy(&detachedAttribute);
				continue;
			}

			pthread_attr_destroy(&detachedAttribute);
#else
			if ( (pid= fork() ) == 0)	// I am the child
			{
				main_passive( (void *) socktemp);
				return;
			}
#endif
			tempaddrinfo= tempaddrinfo->ai_next;
		}

		freeaddrinfo(addrinfo);
	}

	// All the previous calls are no blocking, so the main line of execution goes here
	// and I have to avoid that the program terminates
	while (1)
		pthread_suspend(10*60*1000); // it wakes up every 10 minutes; it seems to me reasonable
}
Exemplo n.º 15
0
Arquivo: rpcapd.c Projeto: CM44/npcap
//! Program main
int main(int argc, char *argv[], char *envp[])
{
char savefile[MAX_LINE + 1];		// name of the file on which we have to save the configuration
int isdaemon= 0;					// Not null if the user wants to run this program as a daemon
int retval;							// keeps the returning value from several functions
char errbuf[PCAP_ERRBUF_SIZE + 1];	// keeps the error string, prior to be printed


	savefile[0]= 0;
	loadfile[0]= 0;
	hostlist[0]= 0;

	// Initialize errbuf
	memset(errbuf, 0, sizeof(errbuf) );

	if (sock_init(errbuf, PCAP_ERRBUF_SIZE) == -1)
	{
		SOCK_ASSERT(errbuf, 1);
		exit(-1);
	}

	strncpy(address, RPCAP_DEFAULT_NETADDR, MAX_LINE);
	strncpy(port, RPCAP_DEFAULT_NETPORT, MAX_LINE);

	// Prepare to open a new server socket
	memset(&mainhints, 0, sizeof(struct addrinfo));

	mainhints.ai_family = PF_UNSPEC;
	mainhints.ai_flags = AI_PASSIVE;	// Ready to a bind() socket
	mainhints.ai_socktype = SOCK_STREAM;

	// Getting the proper command line options
	while ((retval = getopt(argc, argv, "b:dhp:4l:na:s:f:v")) != -1)
	{
		switch (retval)
		{
			case 'b':
				strncpy(address, optarg, MAX_LINE);
				break;
			case 'p':
				strncpy(port, optarg, MAX_LINE);
				break;
			case '4':
				mainhints.ai_family = PF_INET;		// IPv4 server only
				break;
			case 'd':
				isdaemon= 1;
				break;
			case 'n':
				nullAuthAllowed= 1;
				break;
			case 'v':
				passivemode= 0;
				break;
			case 'l':
			{
				strncpy(hostlist, optarg, sizeof(hostlist) );
				break;
			}
			case 'a':
			{
			char *tmpaddress, *tmpport;
			int i= 0;

				tmpaddress= strtok(optarg, RPCAP_HOSTLIST_SEP);

				while ( (tmpaddress != NULL) && (i < MAX_ACTIVE_LIST) )
				{
					tmpport= strtok(NULL, RPCAP_HOSTLIST_SEP);

					snprintf(activelist[i].address, MAX_LINE, tmpaddress);
					
					if ( (tmpport == NULL) || (strcmp(tmpport, "DEFAULT") == 0) ) // the user choose a custom port
						snprintf(activelist[i].port, MAX_LINE, RPCAP_DEFAULT_NETPORT_ACTIVE);
					else
						snprintf(activelist[i].port, MAX_LINE, tmpport);

					tmpaddress = strtok(NULL, RPCAP_HOSTLIST_SEP);

					i++;
				}
				
				if (i > MAX_ACTIVE_LIST)
					SOCK_ASSERT("Only MAX_ACTIVE_LIST active connections are currently supported.", 1);

				// I don't initialize the remaining part of the structure, since
				// it is already zeroed (it is a global var)
				break;
			}
			case 'f':
				strncpy(loadfile, optarg, MAX_LINE);
				break;
			case 's':
				strncpy(savefile, optarg, MAX_LINE);
				break;
			case 'h':
				printusage();
				exit(0);
			default:
				break;
		}
	}

	if (savefile[0])
	{
		if (fileconf_save(savefile) )
			SOCK_ASSERT("Error when saving the configuration to file", 1);
	}

	// If the file does not exist, it keeps the settings provided by the command line
	if (loadfile[0])
		fileconf_read(0);

#ifdef linux
	// SIGTERM (i.e. kill -15) is not generated in WIN32, although it is included for ANSI compatibility
	signal(SIGTERM, main_cleanup);
	signal(SIGCHLD, main_cleanup_childs);
#endif

	// forking a daemon, if it is needed
	if (isdaemon)
	{
	#ifndef WIN32
	int pid;

		// Unix Network Programming, pg 336
		if ( (pid = fork() ) != 0)
			exit(0);		// Parent terminates

		// First child continues
		// Set daemon mode
		setsid();
		
		// generated under unix with 'kill -HUP', needed to reload the configuration
		signal(SIGHUP, fileconf_read);

		if ( (pid = fork() ) != 0)
			exit(0);		// First child terminates

		// LINUX WARNING: the current linux implementation of pthreads requires a management thread
		// to handle some hidden stuff. So, as soon as you create the first thread, two threads are
		// created. Fom this point on, the number of threads active are always one more compared
		// to the number you're expecting

		// Second child continues
//		umask(0);
//		chdir("/");
	#else
		// We use the SIGABRT signal to kill the Win32 service
		signal(SIGABRT, main_cleanup);

		// If this call succeeds, it is blocking on Win32
		if ( svc_start() != 1)
			SOCK_ASSERT(1, "Unable to start the service");

		// When the previous call returns, the entire application has to be stopped.
		exit(0);
	#endif
	}
	else	// Console mode
	{
		// Enable the catching of Ctrl+C
		signal(SIGINT, main_cleanup);

#ifndef WIN32
		// generated under unix with 'kill -HUP', needed to reload the configuration
		// We do not have this kind of signal in Win32
		signal(SIGHUP, fileconf_read);
#endif

		printf("Press CTRL + C to stop the server...\n");
	}

	// If we're a Win32 service, we have already called this function in the service_main
	main_startup();

	// The code should never arrive here (since the main_startup is blocking)
	//  however this avoids a compiler warning
	exit(0);
}
Exemplo n.º 16
0
INT
WSPAPI
WSPCancelBlockingCall(
    OUT LPINT lpErrno
    )

/*++

Routine Description:

    This routine cancels any outstanding blocking operation for this thread.
    It is normally used in two situations:

        1. A WinSock SPI client is processing a message which has been
           received while a service provider is implementing pseudo
           blocking. In this case, WSAIsBlocking() will be true.

        2. A blocking call is in progress, and the WinSock service
           provider has called back to the WinSock SPI client's "blocking
           hook" function (via the callback function retrieved from
           WPUQueryBlockingCallback()), which in turn is invoking this
           function. Such a situation might arise, for instance, in
           implementing a Cancel option for an operation which require an
           extended time to complete.

    In each case, the original blocking call will terminate as soon as
    possible with the error WSAEINTR. (In (1), the termination will not take
    place until Windows message scheduling has caused control to revert back
    to the pseudo blocking routine in WinSock. In (2), the blocking call
    will be terminated as soon as the blocking hook function completes.)

    In the case of a blocking WSPConnect() operation, WinSock will terminate
    the blocking call as soon as possible, but it may not be possible for
    the socket resources to be released until the connection has completed
    (and then been reset) or timed out. This is likely to be noticeable only
    if the WinSock SPI client immediately tries to open a new socket (if no
    sockets are available), or to WSPConnect() to the same peer.

    Canceling an WSPAccept() or a WSPSelect() call does not adversely impact
    the sockets passed to these calls. Only the particular call fails; any
    operation that was legal before the cancel is legal after the cancel,
    and the state of the socket is not affected in any way.

    Canceling any operation other than WSPAccept() and WSPSelect() can leave
    the socket in an indeterminate state. If a WinSock SPI client cancels a
    blocking operation on a socket, the only operation that the WinSock SPI
    client can depend on being able to perform on the socket is a call to
    WSPCloseSocket(), although other operations may work on some WinSock
    service providers. If a WinSock SPI client desires maximum portability,
    it must be careful not to depend on performing operations after a cancel.
    A WinSock SPI client may reset the connection by setting the timeout on
    SO_LINGER to 0 and calling WSPCloseSocket().

    If a cancel operation compromised the integrity of a SOCK_STREAM's data
    stream in any way, the WinSock provider will reset the connection and
    fail all future operations other than WSPCloseSocket() with
    WSAECONNABORTED.

    Note it is acceptable for WSPCancelBlockingCall() to return successfully
    if the blocking network operation completes prior to being canceled. In
    this case, the blocking operation will return successfully as if
    WSPCancelBlockingCall() had never been called. The only way for the
    WinSock SPI client to know with certainty that an operation was actually
    canceled is to check for a return code of WSAEINTR from the blocking call.

Arguments:

    lpErrno - A pointer to the error code.

Return Value:

    The value returned by WSPCancelBlockingCall() is 0 if the operation was
        successfully canceled. Otherwise the value SOCKET_ERROR is returned,
        and a specific error code is available in lpErrno.

--*/

{

    INT err;
    INT result;
    PSOCK_TLS_DATA tlsData;

    SOCK_ENTER( "WSPCancelBlockingCall", lpErrno, NULL, NULL, NULL );

    SOCK_ASSERT( lpErrno != NULL );

    err = SockEnterApi( TRUE, TRUE );

    if( err != NO_ERROR ) {

        SOCK_EXIT( "WSPCancelBlockingCall", SOCKET_ERROR, TRUE );
        *lpErrno = err;
        return SOCKET_ERROR;

    }

    tlsData = SOCK_GET_THREAD_DATA();
    SOCK_ASSERT( tlsData != NULL );

    //
    // This call is only valid when we are in a blocking call.
    //

    if( !tlsData->IsBlocking ) {

        SOCK_EXIT( "WSPCancelBlockingCall", SOCKET_ERROR, TRUE );
        *lpErrno = WSAEINVAL;
        return SOCKET_ERROR;

    }

    //
    // The IO should not have been cancelled yet.
    //

    SOCK_ASSERT( tlsData->ReentrancyFlag );
    SOCK_ASSERT( !tlsData->IoCancelled );
    SOCK_ASSERT( tlsData->BlockingSocketInfo != NULL );

    //
    // Cancel it.
    //

    result = tlsData->BlockingSocketInfo->Hooker->WSACancelBlockingCall();

    if( result == SOCKET_ERROR ) {

        *lpErrno = tlsData->BlockingSocketInfo->Hooker->WSAGetLastError();
        SOCK_EXIT( "WSPCancelBlockingCall", SOCKET_ERROR, TRUE );
        return SOCKET_ERROR;

    }

    //
    // Remember that we've cancelled it.
    //

    tlsData->IoCancelled = TRUE;

    SOCK_EXIT( "WSPCancelBlockingCall", NO_ERROR, FALSE );
    return NO_ERROR;

}   // WSPCancelBlockingCall
Exemplo n.º 17
0
/*
 * Receives a datagram from a socket.
 *
 * Returns the size of the datagram on success or -1 on error.
 */
int sock_recv_dgram(SOCKET sock, void *buffer, size_t size,
    char *errbuf, int errbuflen)
{
	ssize_t nread;
#ifndef _WIN32
	struct msghdr message;
	struct iovec iov;
#endif

	if (size == 0)
	{
		SOCK_ASSERT("I have been requested to read zero bytes", 1);
		return 0;
	}
	if (size > INT_MAX)
	{
		if (errbuf)
		{
			pcap_snprintf(errbuf, errbuflen,
			    "Can't read more than %u bytes with sock_recv_dgram",
			    INT_MAX);
		}
		return -1;
	}

	/*
	 * This should be a datagram socket, so we should get the
	 * entire datagram in one recv() or recvmsg() call, and
	 * don't need to loop.
	 */
#ifdef _WIN32
	nread = recv(sock, buffer, size, 0);
	if (nread == SOCKET_ERROR)
	{
		/*
		 * To quote the MSDN documentation for recv(),
		 * "If the datagram or message is larger than
		 * the buffer specified, the buffer is filled
		 * with the first part of the datagram, and recv
		 * generates the error WSAEMSGSIZE. For unreliable
		 * protocols (for example, UDP) the excess data is
		 * lost..."
		 *
		 * So if the message is bigger than the buffer
		 * supplied to us, the excess data is discarded,
		 * and we'll report an error.
		 */
		sock_geterror("recv(): ", errbuf, errbuflen);
		return -1;
	}
#else /* _WIN32 */
	/*
	 * The Single UNIX Specification says that a recv() on
	 * a socket for a message-oriented protocol will discard
	 * the excess data.  It does *not* indicate that the
	 * receive will fail with, for example, EMSGSIZE.
	 *
	 * Therefore, we use recvmsg(), which appears to be
	 * the only way to get a "message truncated" indication
	 * when receiving a message for a message-oriented
	 * protocol.
	 */
	message.msg_name = NULL;	/* we don't care who it's from */
	message.msg_namelen = 0;
	iov.iov_base = buffer;
	iov.iov_len = size;
	message.msg_iov = &iov;
	message.msg_iovlen = 1;
#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
	message.msg_control = NULL;	/* we don't care about control information */
	message.msg_controllen = 0;
#endif
#ifdef HAVE_STRUCT_MSGHDR_MSG_FLAGS
	message.msg_flags = 0;
#endif
	nread = recvmsg(sock, &message, 0);
	if (nread == -1)
	{
		if (errno == EINTR)
			return -3;
		sock_geterror("recv(): ", errbuf, errbuflen);
		return -1;
	}
#ifdef HAVE_STRUCT_MSGHDR_MSG_FLAGS
	/*
	 * XXX - Solaris supports this, but only if you ask for the
	 * X/Open version of recvmsg(); should we use that, or will
	 * that cause other problems?
	 */
	if (message.msg_flags & MSG_TRUNC)
	{
		/*
		 * Message was bigger than the specified buffer size.
		 *
		 * Report this as an error, as the Microsoft documentation
		 * implies we'd do in a similar case on Windows.
		 */
		pcap_snprintf(errbuf, errbuflen, "recv(): Message too long");
		return -1;
	}
#endif /* HAVE_STRUCT_MSGHDR_MSG_FLAGS */
#endif /* _WIN32 */

	/*
	 * The size we're reading fits in an int, so the return value
	 * will fit in an int.
	 */
	return (int)nread;
}
Exemplo n.º 18
0
Arquivo: rpcapd.c Projeto: CM44/npcap
/*!
	\brief 'true' main of the program.

	It must be in a separate function because:
	- if we're in 'console' mode, we have to put the main thread waiting for a Ctrl+C
	(in order to be able to stop everything)
	- if we're in daemon mode, the main program must terminate and a new child must be 
	created in order to create the daemon

	\param ptr: it keeps the main socket handler (what's called 'sockmain' in the main() ), that
	represents the socket used in the main connection. It is a 'void *' just because pthreads
	want this format.
*/
void main_passive(void *ptr)
{
char errbuf[PCAP_ERRBUF_SIZE + 1];	// keeps the error string, prior to be printed
SOCKET sockctrl;				// keeps the socket ID for this control connection
struct sockaddr_storage from;	// generic sockaddr_storage variable
socklen_t fromlen;				// keeps the length of the sockaddr_storage variable
SOCKET sockmain;

#ifndef WIN32
	pid_t pid;
#endif

	sockmain= *((SOCKET *) ptr);

	// Delete the pointer (which has been allocated in the main)
	free(ptr);

	// Initialize errbuf
	memset(errbuf, 0, sizeof(errbuf) );

	// main thread loop
	while (1)
	{
#ifdef WIN32
	pthread_t threadId;					// Pthread variable that keeps the thread structures
	pthread_attr_t detachedAttribute;
#endif
	struct daemon_slpars *pars;			// parameters needed by the daemon_serviceloop()

		// Connection creation
		fromlen = sizeof(struct sockaddr_storage);

		sockctrl= accept(sockmain, (struct sockaddr *) &from, &fromlen);
		
		if (sockctrl == -1)
		{
			// The accept() call can return this error when a signal is catched
			// In this case, we have simply to ignore this error code
			// Stevens, pg 124
#ifdef WIN32
			if (WSAGetLastError() == WSAEINTR)
#else
			if (errno == EINTR)
#endif
				continue;

			// Don't check for errors here, since the error can be due to the fact that the thread 
			// has been killed
			sock_geterror("accept(): ", errbuf, PCAP_ERRBUF_SIZE);
			SOCK_ASSERT(errbuf, 1);
			continue;
		}

		// checks if the connecting host is among the ones allowed
		if (sock_check_hostlist(hostlist, RPCAP_HOSTLIST_SEP, &from, errbuf, PCAP_ERRBUF_SIZE) < 0 )
		{
			rpcap_senderror(sockctrl, errbuf, PCAP_ERR_HOSTNOAUTH, NULL);
			sock_close(sockctrl, NULL, 0);
			continue;
		}


#ifdef WIN32
		// in case of passive mode, this variable is deallocated by the daemon_serviceloop()
		pars= (struct daemon_slpars *) malloc ( sizeof(struct daemon_slpars) );
		if (pars == NULL)
		{
			snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno));
			continue;
		}

		pars->sockctrl= sockctrl;
		pars->activeclose= 0;		// useless in passive mode
		pars->isactive= 0;
		pars->nullAuthAllowed= nullAuthAllowed;

		/* GV we need this to create the thread as detached. */
		/* GV otherwise, the thread handle is not destroyed  */
		pthread_attr_init(&detachedAttribute); 
		pthread_attr_setdetachstate(&detachedAttribute, PTHREAD_CREATE_DETACHED);
		if ( pthread_create( &threadId, &detachedAttribute, (void *) &daemon_serviceloop, (void *) pars) )
		{
			SOCK_ASSERT("Error creating the child thread", 1);
			pthread_attr_destroy(&detachedAttribute);
			continue;
		}
		pthread_attr_destroy(&detachedAttribute);

#else
		if ( (pid= fork() ) == 0)	// I am the child
		{
			// in case of passive mode, this variable is deallocated by the daemon_serviceloop()
			pars= (struct daemon_slpars *) malloc ( sizeof(struct daemon_slpars) );
			if (pars == NULL)
			{
				snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno));
				exit(0);
			}

			pars->sockctrl= sockctrl;
			pars->activeclose= 0;		// useless in passive mode
			pars->isactive= 0;
			pars->nullAuthAllowed= nullAuthAllowed;

			// Close the main socket (must be open only in the parent)
			closesocket(sockmain);

			daemon_serviceloop( (void *) pars);
			exit(0);
		}

		// I am the parent
		// Close the childsocket (must be open only in the child)
		closesocket(sockctrl);
#endif

		// loop forever, until interrupted
	}
}
Exemplo n.º 19
0
Arquivo: rpcapd.c Projeto: CM44/npcap
/*!
	\brief 'true' main of the program in case the active mode is turned on.

	It does not have any return value nor parameters.
	This function loops forever trying to connect to the remote host, until the
	daemon is turned down.

	\param ptr: it keeps the 'activepars' parameters. It is a 'void *' just because pthreads
	want this format.
*/
void main_active(void *ptr)
{
char errbuf[PCAP_ERRBUF_SIZE + 1];	// keeps the error string, prior to be printed
SOCKET sockctrl;					// keeps the socket ID for this control connection
struct addrinfo hints;				// temporary struct to keep settings needed to open the new socket
struct addrinfo *addrinfo;			// keeps the addrinfo chain; required to open a new socket
struct active_pars *activepars;
struct daemon_slpars *pars;			// parameters needed by the daemon_serviceloop()


	activepars= (struct active_pars *) ptr;

	// Prepare to open a new server socket
	memset(&hints, 0, sizeof(struct addrinfo));
									// WARNING Currently it supports only ONE socket family among IPv4 and IPv6 
	hints.ai_family = AF_INET;		// PF_UNSPEC to have both IPv4 and IPv6 server
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_family= activepars->ai_family;

	snprintf(errbuf, PCAP_ERRBUF_SIZE, "Connecting to host %s, port %s, using protocol %s",
			activepars->address, activepars->port, (hints.ai_family == AF_INET) ? "IPv4": 
			(hints.ai_family == AF_INET6) ? "IPv6" : "Unspecified");
	SOCK_ASSERT(errbuf, 1);

	// Initialize errbuf
	memset(errbuf, 0, sizeof(errbuf) );

	// Do the work
	if (sock_initaddress(activepars->address, activepars->port, &hints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1)
	{
		SOCK_ASSERT(errbuf, 1);
		return;
	}

	while (1)
	{
	int activeclose;

		if ( (sockctrl= sock_open(addrinfo, SOCKOPEN_CLIENT, 0, errbuf, PCAP_ERRBUF_SIZE)) == -1)
		{
			SOCK_ASSERT(errbuf, 1);

			snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error connecting to host %s, port %s, using protocol %s",
					activepars->address, activepars->port, (hints.ai_family == AF_INET) ? "IPv4": 
					(hints.ai_family == AF_INET6) ? "IPv6" : "Unspecified" );

			SOCK_ASSERT(errbuf, 1);

			pthread_suspend(RPCAP_ACTIVE_WAIT * 1000);

			continue;
		}

		pars= (struct daemon_slpars *) malloc ( sizeof(struct daemon_slpars) );
		if (pars == NULL)
		{
			snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno));
			continue;
		}

		pars->sockctrl= sockctrl;
		pars->activeclose= 0;
		pars->isactive= 1;
		pars->nullAuthAllowed= nullAuthAllowed;

		daemon_serviceloop( (void *) pars);

		activeclose= pars->activeclose;

		free(pars);

		// If the connection is closed by the user explicitely, don't try to connect to it again
		// just exit the program
		if (activeclose == 1)
			break;
	}
}
/*!
	\brief Checks that one host (identified by the sockaddr_storage structure) belongs to an 'allowed list'.

	This function is useful after an accept() call in order to check if the connecting
	host is allowed to connect to me. To do that, we have a buffer that keeps the list of the
	allowed host; this function checks the sockaddr_storage structure of the connecting host
	against this host list, and it returns '0' is the host is included in this list.

	\param hostlist: pointer to a string that contains the list of the allowed host.

	\param sep: a string that keeps the separators used between the hosts (for example the
	space character) in the host list.

	\param from: a sockaddr_storage structure, as it is returned by the accept() call.

	\param errbuf: a pointer to an user-allocated buffer that will contain the complete
	error message. This buffer has to be at least 'errbuflen' in length.
	It can be NULL; in this case the error cannot be printed.

	\param errbuflen: length of the buffer that will contains the error. The error message cannot be
	larger than 'errbuflen - 1' because the last char is reserved for the string terminator.

	\return It returns:
	- '1' if the host list is empty
	- '0' if the host belongs to the host list (and therefore it is allowed to connect)
	- '-1' in case the host does not belong to the host list (and therefore it is not allowed to connect
	- '-2' in case or error. The error message is returned in the 'errbuf' variable.
*/
int sock_check_hostlist(char *hostlist, const char *sep, struct sockaddr_storage *from, char *errbuf, int errbuflen)
{
    // checks if the connecting host is among the ones allowed
    if ( (hostlist) && (hostlist[0]) )
    {
        char *token;					// temp, needed to separate items into the hostlist
        struct addrinfo *addrinfo, *ai_next;
        char *temphostlist;

        temphostlist= (char *) malloc (strlen(hostlist) + 1);
        if (temphostlist == NULL)
        {
            sock_geterror("sock_check_hostlist(), malloc() failed", errbuf, errbuflen);
            return -2;
        }

        // The problem is that strtok modifies the original variable by putting '0' at the end of each token
        // So, we have to create a new temporary string in which the original content is kept
        strcpy(temphostlist, hostlist);

        token= strtok(temphostlist, sep);

        // it avoids a warning in the compilation ('addrinfo used but not initialized')
        addrinfo = NULL;

        while( token != NULL )
        {
            struct addrinfo hints;
            int retval;

            addrinfo = NULL;
            memset(&hints, 0, sizeof (struct addrinfo) );
            hints.ai_family = PF_UNSPEC;
            hints.ai_socktype= SOCK_STREAM;

            retval = getaddrinfo(token, "0", &hints, &addrinfo);
            if (retval != 0)
            {
                if (errbuf)
                {
                    snprintf(errbuf, errbuflen, "getaddrinfo() %s", gai_strerror(retval));
                    errbuf[errbuflen - 1]= 0;
                }

                SOCK_ASSERT(errbuf, 1);

                // Get next token
                token = strtok( NULL, sep);
                continue;
            }

            // ai_next is required to preserve the content of addrinfo, in order to deallocate it properly
            ai_next= addrinfo;
            while(ai_next)
            {
                if (sock_cmpaddr(from, (struct sockaddr_storage *) ai_next->ai_addr) == 0)
                {
                    free(temphostlist);
                    return 0;
                }

                // If we are here, it means that the current address does not matches
                // Let's try with the next one in the header chain
                ai_next= ai_next->ai_next;
            }

            freeaddrinfo(addrinfo);
            addrinfo= NULL;

            // Get next token
            token = strtok( NULL, sep);
        }

        if (addrinfo)
        {
            freeaddrinfo(addrinfo);
            addrinfo= NULL;
        }

        if (errbuf)
        {
            snprintf(errbuf, errbuflen, "The host is not in the allowed host list. Connection refused.");
            errbuf[errbuflen - 1]= 0;
        }

        free(temphostlist);
        return -1;
    }

    // No hostlist, so we have to return 'empty list'
    return 1;
}
Exemplo n.º 21
0
INT
WSPAPI
WSPStringToAddress(
    IN LPWSTR AddressString,
    IN INT AddressFamily,
    IN LPWSAPROTOCOL_INFOW lpProtocolInfo,
    OUT LPSOCKADDR lpAddress,
    IN OUT LPINT lpAddressLength,
    OUT LPINT lpErrno
    )

/*++

Routine Description:

    This routine converts a human-readable string to a socket address
    structure (SOCKADDR) suitable for pass to Windows Sockets routines which
    take such a structure. Any missing components of the address will be
    defaulted to a reasonable value if possible. For example, a missing port
    number will be defaulted to zero.

Arguments:

    AddressString - Points to the zero-terminated human-readable string to
        convert.

    AddressFamily - The address family to which the string belongs, or
        AF_UNSPEC if it is unknown.

    lpProtocolInfo - The provider's WSAPROTOCOL_INFOW struct.

    lpAddress - A buffer which is filled with a single SOCKADDR structure.

    lpAddressLength - The length of the Address buffer. Returns the size of
        the resultant SOCKADDR structure.

    lpErrno - A pointer to the error code.

Return Value:

    If no error occurs, WSPStringToAddress() returns 0. Otherwise, a value
        of SOCKET_ERROR is returned, and a specific error code is available
        in lpErrno.

--*/

{

    INT err;
    INT result;
    LPWSTR terminator;
    ULONG ipAddress;
    USHORT port;
    LPSOCKADDR_IN addr;

    SOCK_ENTER( "WSPStringToAddress", AddressString, (PVOID)AddressFamily, lpProtocolInfo, lpAddress );

    SOCK_ASSERT( lpErrno != NULL );

    err = SockEnterApi( TRUE, FALSE );

    if( err != NO_ERROR ) {

        SOCK_EXIT( "WSPStringToAddress", SOCKET_ERROR, TRUE );
        *lpErrno = err;
        return SOCKET_ERROR;

    }

    //
    // Quick sanity check.
    //

    if( AddressString == NULL ||
        lpAddress == NULL ||
        lpAddressLength == NULL ||
        *lpAddressLength < sizeof(SOCKADDR_IN) ) {

        err = WSAEFAULT;
        goto exit;

    }

    if( AddressFamily != AF_INET ) {

        err = WSA_INVALID_PARAMETER;
        goto exit;

    }

    //
    // Convert it.
    //

    //
    // BUGBUG: VERIFY THE WCHAR CRT FUNCTIONS WORK UNDER WIN95!
    //

    ipAddress = MyInetAddr( AddressString, &terminator );

    if( ipAddress == INADDR_NONE ) {

        err = WSA_INVALID_PARAMETER;
        goto exit;

    }

    if( *terminator == L':' ) {

        WCHAR ch;
        USHORT base;

        terminator++;

        port = 0;
        base = 10;

        if( *terminator == L'0' ) {
            base = 8;
            terminator++;

            if( *terminator == L'x' ) {
                base = 16;
                terminator++;
            }
        }

        while( ch = *terminator++ ) {
            if( iswdigit(ch) ) {
                port = ( port * base ) + ( ch - L'0' );
            } else if( base == 16 && iswxdigit(ch) ) {
                port = ( port << 4 );
                port += ch + 10 - ( iswlower(ch) ? L'a' : L'A' );
            } else {
                return WSA_INVALID_PARAMETER;
            }
        }

    } else {
        port = 0;
    }

    //
    // Build the address.
    //

    ZeroMemory(
        lpAddress,
        sizeof(SOCKADDR_IN)
        );

    addr = (LPSOCKADDR_IN)lpAddress;
    *lpAddressLength = sizeof(SOCKADDR_IN);

    addr->sin_family = AF_INET;
    addr->sin_port = port;
    addr->sin_addr.s_addr = ipAddress;

    //
    // Success!
    //

    SOCK_ASSERT( err == NO_ERROR );

    result = 0;

exit:

    if( err != NO_ERROR ) {

        *lpErrno = err;
        result = SOCKET_ERROR;

    }

    SOCK_EXIT( "WSPAddressToString", result, (BOOL)( result == SOCKET_ERROR ) );
    return result;

}   // WSPStringToAddress