예제 #1
0
/*
 * \brief Checks that the address, port and flags given are valids and it returns an 'addrinfo' structure.
 *
 * This function basically calls the getaddrinfo() calls, and it performs a set of sanity checks
 * to control that everything is fine (e.g. a TCP socket cannot have a mcast address, and such).
 * If an error occurs, it writes the error message into 'errbuf'.
 *
 * \param host: a pointer to a string identifying the host. It can be
 * a host name, a numeric literal address, or NULL or "" (useful
 * in case of a server socket which has to bind to all addresses).
 *
 * \param port: a pointer to a user-allocated buffer containing the network port to use.
 *
 * \param hints: an addrinfo variable (passed by reference) containing the flags needed to create the
 * addrinfo structure appropriately.
 *
 * \param addrinfo: it represents the true returning value. This is a pointer to an addrinfo variable
 * (passed by reference), which will be allocated by this function and returned back to the caller.
 * This variable will be used in the next sockets calls.
 *
 * \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. The addrinfo variable that has to be used in the following sockets calls is
 * returned into the addrinfo parameter.
 *
 * \warning The 'addrinfo' variable has to be deleted by the programmer by calling freeaddrinfo() when
 * it is no longer needed.
 *
 * \warning This function requires the 'hints' variable as parameter. The semantic of this variable is the same
 * of the one of the corresponding variable used into the standard getaddrinfo() socket function. We suggest
 * the programmer to look at that function in order to set the 'hints' variable appropriately.
 */
int sock_initaddress(const char *host, const char *port,
    struct addrinfo *hints, struct addrinfo **addrinfo, char *errbuf, int errbuflen)
{
	int retval;

	retval = getaddrinfo(host, port, hints, addrinfo);
	if (retval != 0)
	{
		/*
		 * if the getaddrinfo() fails, you have to use gai_strerror(), instead of using the standard
		 * error routines (errno) in UNIX; Winsock suggests using the GetLastError() instead.
		 */
		if (errbuf)
		{
#ifdef _WIN32
			sock_geterror("getaddrinfo(): ", errbuf, errbuflen);
#else
			pcap_snprintf(errbuf, errbuflen, "getaddrinfo() %s", gai_strerror(retval));
#endif
		}
		return -1;
	}
	/*
	 * \warning SOCKET: I should check all the accept() in order to bind to all addresses in case
	 * addrinfo has more han one pointers
	 */

	/*
	 * This software only supports PF_INET and PF_INET6.
	 *
	 * XXX - should we just check that at least *one* address is
	 * either PF_INET or PF_INET6, and, when using the list,
	 * ignore all addresses that are neither?  (What, no IPX
	 * support? :-))
	 */
	if (((*addrinfo)->ai_family != PF_INET) &&
	    ((*addrinfo)->ai_family != PF_INET6))
	{
		if (errbuf)
			pcap_snprintf(errbuf, errbuflen, "getaddrinfo(): socket type not supported");
		freeaddrinfo(*addrinfo);
		*addrinfo = NULL;
		return -1;
	}

	/*
	 * You can't do multicast (or broadcast) TCP.
	 */
	if (((*addrinfo)->ai_socktype == SOCK_STREAM) &&
	    (sock_ismcastaddr((*addrinfo)->ai_addr) == 0))
	{
		if (errbuf)
			pcap_snprintf(errbuf, errbuflen, "getaddrinfo(): multicast addresses are not valid when using TCP streams");
		freeaddrinfo(*addrinfo);
		*addrinfo = NULL;
		return -1;
	}

	return 0;
}
/*!
	\brief It sends the amount of data contained into 'buffer' on the given socket.

	This function basically calls the send() socket function and it checks that all
	the data specified in 'buffer' (of size 'size') will be sent. If an error occurs,
	it writes the error message into 'errbuf'.
	In case the socket buffer does not have enough space, it loops until all data
	has been sent.

	\param socket: the connected socket currently opened.

	\param buffer: a char pointer to a user-allocated buffer in which data is contained.

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

	\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_send(SOCKET socket, const char *buffer, int size, char *errbuf, int errbuflen)
{
    int nsent;

send:
#ifdef linux
    /*
    	Another pain... in Linux there's this flag
    	MSG_NOSIGNAL
    		Requests not to send SIGPIPE on errors on stream-oriented
    		sockets when the other end breaks the connection.
    		The EPIPE error is still returned.
    */
    nsent = send(socket, buffer, size, MSG_NOSIGNAL);
#else
    nsent = send(socket, buffer, size, 0);
#endif

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

    if (nsent != size)
    {
        size-= nsent;
        buffer+= nsent;
        goto send;
    }

    return 0;
}
/*!
	\brief It retrieves two strings containing the address and the port of a given 'sockaddr' variable.

	This function is basically an extended version of the inet_ntop(), which does not exist in
	WIN32 because the same result can be obtained by using the getnameinfo().
	However, differently from inet_ntop(), this function is able to return also literal names
	(e.g. 'locahost') dependingly from the 'Flags' parameter.

	The function accepts a sockaddr_storage variable (which can be returned by several functions
	like bind(), connect(), accept(), and more) and it transforms its content into a 'human'
	form. So, for instance, it is able to translate an hex address (stored in bynary form) into
	a standard IPv6 address like "::1".

	The behaviour of this function depends on the parameters we have in the 'Flags' variable, which
	are the ones allowed in the standard getnameinfo() socket function.

	\param sockaddr: a 'sockaddr_in' or 'sockaddr_in6' structure containing the address that
	need to be translated from network form into the presentation form. This structure must be
	zero-ed prior using it, and the address family field must be filled with the proper value.
	The user must cast any 'sockaddr_in' or 'sockaddr_in6' structures to 'sockaddr_storage' before
	calling this function.

	\param address: it contains the address that will be returned by the function. This buffer
	must be properly allocated by the user. The address can be either literal or numeric depending
	on the value of 'Flags'.

	\param addrlen: the length of the 'address' buffer.

	\param port: it contains the port that will be returned by the function. This buffer
	must be properly allocated by the user.

	\param portlen: the length of the 'port' buffer.

	\param flags: a set of flags (the ones defined into the getnameinfo() standard socket function)
	that determine if the resulting address must be in numeric / literal form, and so on.

	\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 this function succeedes, '0' otherwise.
	The address and port corresponding to the given SockAddr are returned back in the buffers 'address'
	and 'port'.
	In any case, the returned strings are '0' terminated.
*/
int sock_getascii_addrport(const struct sockaddr_storage *sockaddr, char *address, int addrlen, char *port, int portlen, int flags, char *errbuf, int errbuflen)
{
    socklen_t sockaddrlen;
    int retval;					// Variable that keeps the return value;

    retval= -1;

#ifdef WIN32
    if (sockaddr->ss_family == AF_INET)
        sockaddrlen = sizeof(struct sockaddr_in);
    else
        sockaddrlen = sizeof(struct sockaddr_in6);
#else
    sockaddrlen = sizeof(struct sockaddr_storage);
#endif

    if ((flags & NI_NUMERICHOST) == 0)	// Check that we want literal names
    {
        if ( (sockaddr->ss_family == AF_INET6) &&
                (memcmp( &((struct sockaddr_in6 *) sockaddr)->sin6_addr, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", sizeof(struct in6_addr) ) == 0) )
        {
            if (address)
                strncpy(address, SOCKET_NAME_NULL_DAD, addrlen);
            return retval;
        }
    }

    if ( getnameinfo((struct sockaddr *) sockaddr, sockaddrlen, address, addrlen, port, portlen, flags) != 0)
    {
        // If the user wants to receive an error message
        if (errbuf)
        {
            sock_geterror("getnameinfo(): ", errbuf, errbuflen);
            errbuf[errbuflen-1]= 0;
        }

        if (address)
        {
            strncpy(address, SOCKET_NO_NAME_AVAILABLE, addrlen);
            address[addrlen-1]= 0;
        }

        if (port)
        {
            strncpy(port, SOCKET_NO_PORT_AVAILABLE, portlen);
            port[portlen-1]= 0;
        }

        retval= 0;
    }

    return retval;
}
/*!
	\brief Checks that the address, port and flags given are valids and it returns an 'addrinfo' stucture.

	This function basically calls the getaddrinfo() calls, and it performs a set of sanity checks
	to control that everything is fine (e.g. a TCP socket cannot have a mcast address, and such).
	If an error occurs, it writes the error message into 'errbuf'.

	\param address: a pointer to a user-allocated buffer containing the network address to check.
	It could be both a numeric - literal address, and it can be NULL or "" (useful in case of a server
	socket which has to bind to all addresses).

	\param port: a pointer to a user-allocated buffer containing the network port to use.

	\param hints: an addrinfo variable (passed by reference) containing the flags needed to create the
	addrinfo structure appropriately.

	\param addrinfo: it represents the true returning value. This is a pointer to an addrinfo variable
	(passed by reference), which will be allocated by this function and returned back to the caller.
	This variable will be used in the next sockets calls.

	\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. The addrinfo variable that has to be used in the following sockets calls is
	returned into the addrinfo parameter.

	\warning The 'addrinfo' variable has to be deleted by the programmer by calling freeaddrinfo() when
	it is no longer needed.

	\warning This function requires the 'hints' variable as parameter. The semantic of this variable is the same
	of the one of the corresponding variable used into the standard getaddrinfo() socket function. We suggest
	the programmer to look at that function in order to set the 'hints' variable appropriately.
*/
int sock_initaddress(const char *address, const char *port,
                     struct addrinfo *hints, struct addrinfo **addrinfo, char *errbuf, int errbuflen)
{
    int retval;

    retval = getaddrinfo(address, port, hints, addrinfo);
    if (retval != 0)
    {
        // if the getaddrinfo() fails, you have to use gai_strerror(), instead of using the standard
        // error routines (errno) in UNIX; WIN32 suggests using the GetLastError() instead.
        if (errbuf)
#ifdef WIN32
            sock_geterror("getaddrinfo(): ", errbuf, errbuflen);
#else
            if (errbuf)
            {
                snprintf(errbuf, errbuflen, "getaddrinfo() %s", gai_strerror(retval));
                errbuf[errbuflen - 1]= 0;
            }
#endif
        return -1;
    }
    /*!
    	\warning SOCKET: I should check all the accept() in order to bind to all addresses in case
    	addrinfo has more han one pointers
    */

    // This software only supports PF_INET and PF_INET6.
    if (( (*addrinfo)->ai_family != PF_INET) && ( (*addrinfo)->ai_family != PF_INET6))
    {
        if (errbuf)
        {
            snprintf(errbuf, errbuflen, "getaddrinfo(): socket type not supported");
            errbuf[errbuflen - 1]= 0;
        }
        return -1;
    }

    if ( ( (*addrinfo)->ai_socktype == SOCK_STREAM) && (sock_ismcastaddr( (*addrinfo)->ai_addr) == 0) )
    {
        if (errbuf)
        {
            snprintf(errbuf, errbuflen, "getaddrinfo(): multicast addresses are not valid when using TCP streams");
            errbuf[errbuflen - 1]= 0;
        }

        return -1;
    }

    return 0;
}
/*!
	\brief Closes the present (TCP and UDP) socket connection.

	This function sends a shutdown() on the socket in order to disable send() calls
	(while recv() ones are still allowed). Then, it closes the socket.

	\param sock: the socket identifier of the connection that has to be closed.

	\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_close(SOCKET sock, char *errbuf, int errbuflen)
{
    // SHUT_WR: subsequent calls to the send function are disallowed.
    // For TCP sockets, a FIN will be sent after all data is sent and
    // acknowledged by the Server.
    if (shutdown(sock, SHUT_WR) )
    {
        sock_geterror("shutdown(): ", errbuf, errbuflen);
        // close the socket anyway
        closesocket(sock);
        return -1;
    }

    closesocket(sock);
    return 0;
}
예제 #6
0
/*
 * \brief It gets the address/port the system picked for this socket (on connected sockets).
 *
 * It is used to return the address and port the server picked for our socket on the local machine.
 * It works only on:
 * - connected sockets
 * - server sockets
 *
 * On unconnected client sockets it does not work because the system dynamically chooses a port
 * only when the socket calls a send() call.
 *
 * \param sock: the connected socket currently opened.
 *
 * \param address: it contains the address that will be returned by the function. This buffer
 * must be properly allocated by the user. The address can be either literal or numeric depending
 * on the value of 'Flags'.
 *
 * \param addrlen: the length of the 'address' buffer.
 *
 * \param port: it contains the port that will be returned by the function. This buffer
 * must be properly allocated by the user.
 *
 * \param portlen: the length of the 'port' buffer.
 *
 * \param flags: a set of flags (the ones defined into the getnameinfo() standard socket function)
 * that determine if the resulting address must be in numeric / literal form, and so on.
 *
 * \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 this function succeeds, '0' otherwise.
 * The address and port corresponding are returned back in the buffers 'address' and 'port'.
 * In any case, the returned strings are '0' terminated.
 *
 * \warning If the socket is using a connectionless protocol, the address may not be available
 * until I/O occurs on the socket.
 */
int sock_getmyinfo(SOCKET sock, char *address, int addrlen, char *port, int portlen, int flags, char *errbuf, int errbuflen)
{
	struct sockaddr_storage mysockaddr;
	socklen_t sockaddrlen;


	sockaddrlen = sizeof(struct sockaddr_storage);

	if (getsockname(sock, (struct sockaddr *) &mysockaddr, &sockaddrlen) == -1)
	{
		sock_geterror("getsockname(): ", errbuf, errbuflen);
		return 0;
	}

	/* Returns the numeric address of the host that triggered the error */
	return sock_getascii_addrport(&mysockaddr, address, addrlen, port, portlen, flags, errbuf, errbuflen);
}
/*!
	\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;
}
/*!
	\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;
}
/*!
	\brief It initializes a network connection both from the client and the server side.

	In case of a client socket, this function calls socket() and connect().
	In the meanwhile, it checks for any socket error.
	If an error occurs, it writes the error message into 'errbuf'.

	In case of a server socket, the function calls socket(), bind() and listen().

	This function is usually preceeded by the sock_initaddress().

	\param addrinfo: pointer to an addrinfo variable which will be used to
	open the socket and such. This variable is the one returned by the previous call to
	sock_initaddress().

	\param server: '1' if this is a server socket, '0' otherwise.

	\param nconn: number of the connections that are allowed to wait into the listen() call.
	This value has no meanings in case of a client socket.

	\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 socket that has been opened (that has to be used in the following sockets calls)
	if everything is fine, '0' if some errors occurred. The error message is returned
	in the 'errbuf' variable.
*/
SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf, int errbuflen)
{
    SOCKET sock;

    sock = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
    if (sock == -1)
    {
        sock_geterror("socket(): ", errbuf, errbuflen);
        return -1;
    }


    // This is a server socket
    if (server)
    {
#ifdef BSD
        // Force the use of IPv6-only addresses; in BSD you can accept both v4 and v6
        // connections if you have a "NULL" pointer as the nodename in the getaddrinfo()
        // This behaviour is not clear in the RFC 2553, so each system implements the
        // bind() differently from this point of view

        if (addrinfo->ai_family == PF_INET6)
        {
            int on;

            if (setsockopt(sock, IPPROTO_IPV6, IPV6_BINDV6ONLY, (char *)&on, sizeof (int)) == -1)
            {
                if (errbuf)
                {
                    snprintf(errbuf, errbuflen, "setsockopt(IPV6_BINDV6ONLY)");
                    errbuf[errbuflen - 1]= 0;
                }
                return -1;
            }
        }
#endif

#ifndef WIN32
        //You have to reuse socket if anybody was using it before you
        {
            int on = 1;
            setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (int));
        }
#endif


        // WARNING: if the address is a mcast one, I should place the proper Win32 code here
        if (bind(sock, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0)
        {
            sock_geterror("bind(): ", errbuf, errbuflen);
            return -1;
        }

        if (addrinfo->ai_socktype == SOCK_STREAM)
            if (listen(sock, nconn) == -1)
            {
                sock_geterror("listen(): ", errbuf, errbuflen);
                return -1;
            }

        // server side ended
        return sock;
    }
    else	// we're the client
    {
        struct addrinfo *tempaddrinfo;
        char *errbufptr;
        size_t bufspaceleft;

        tempaddrinfo= addrinfo;
        errbufptr= errbuf;
        bufspaceleft= errbuflen;
        *errbufptr= 0;


        // We have to loop though all the addinfo returned.
        // For instance, we can have both IPv6 and IPv4 addresses, but the service we're trying
        // to connect to is unavailable in IPv6, so we have to try in IPv4 as well
        while (tempaddrinfo)
        {

            if (connect(sock, tempaddrinfo->ai_addr, tempaddrinfo->ai_addrlen) == -1)
            {
                size_t msglen;
                char TmpBuffer[100];
                char SocketErrorMessage[SOCK_ERRBUF_SIZE];

                // We have to retrieve the error message before any other socket call completes, otherwise
                // the error message is lost
                sock_geterror(NULL, SocketErrorMessage, sizeof(SocketErrorMessage) );

                // Returns the numeric address of the host that triggered the error
                sock_getascii_addrport( (struct sockaddr_storage *) tempaddrinfo->ai_addr, TmpBuffer, sizeof(TmpBuffer), NULL, 0, NI_NUMERICHOST, TmpBuffer, sizeof(TmpBuffer) );

                snprintf(errbufptr, bufspaceleft, "Is the server properly installed on %s?  connect() failed: %s", TmpBuffer, SocketErrorMessage);

                // In case more then one 'connect' fails, we manage to keep all the error messages
                msglen= strlen(errbufptr);

                errbufptr[msglen]= ' ';
                errbufptr[msglen + 1]= 0;

                bufspaceleft= bufspaceleft - (msglen + 1);
                errbufptr+= (msglen + 1);

                tempaddrinfo= tempaddrinfo->ai_next;
            }
            else
                break;
        }

        // Check how we exit from the previous loop
        // If tempaddrinfo is equal to NULL, it means that all the connect() failed.
        if (tempaddrinfo == NULL)
        {
            closesocket(sock);
            return -1;
        }
        else
            return sock;
    }
}
예제 #10
0
파일: daemon.c 프로젝트: OPEXGroup/winpcap
/*!
	\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
	}
}
예제 #11
0
파일: daemon.c 프로젝트: OPEXGroup/winpcap
/*
	\param plen: the length of the current message (needed in order to be able
	to discard excess data in the message, if present)
*/
pcap_t *daemon_startcapture(SOCKET sockctrl, pthread_t *threaddata, char *source, int active, struct rpcap_sampling *samp_param, uint32 plen, char *errbuf)
{
char portdata[PCAP_BUF_SIZE];		// temp variable needed to derive the data port
char peerhost[PCAP_BUF_SIZE];		// temp variable needed to derive the host name of our peer
pcap_t *fp= NULL;					// pcap_t main variable
unsigned int nread;					// number of bytes of the payload read from the socket
char sendbuf[RPCAP_NETBUF_SIZE];	// temporary buffer in which data to be sent is buffered
int sendbufidx= 0;					// index which keeps the number of bytes currently buffered

// socket-related variables
SOCKET sockdata= 0;					// socket descriptor of the data connection
struct addrinfo hints;				// temp, needed to open a socket connection
struct addrinfo *addrinfo;			// temp, needed to open a socket connection
struct sockaddr_storage saddr;		// temp, needed to retrieve the network data port chosen on the local machine
socklen_t saddrlen;					// temp, needed to retrieve the network data port chosen on the local machine

pthread_attr_t detachedAttribute;	// temp, needed to set the created thread as detached

// RPCAP-related variables
struct rpcap_startcapreq startcapreq;		// start capture request message
struct rpcap_startcapreply *startcapreply;	// start capture reply message
int serveropen_dp;							// keeps who is going to open the data connection

	addrinfo= NULL;

	if ( (nread= sock_recv(sockctrl, (char *) &startcapreq, sizeof(struct rpcap_startcapreq), SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE)) == -1)
		return NULL;

	startcapreq.flags= ntohs(startcapreq.flags);

	// Open the selected device
	if ( (fp= pcap_open(source, 
			ntohl(startcapreq.snaplen),
			(startcapreq.flags & RPCAP_STARTCAPREQ_FLAG_PROMISC) ? PCAP_OPENFLAG_PROMISCUOUS : 0 /* local device, other flags not needed */, 
			ntohl(startcapreq.read_timeout),
			NULL /* local device, so no auth */,
			errbuf)) == NULL)
	{
		rpcap_senderror(sockctrl, errbuf, PCAP_ERR_OPEN, NULL);
		return NULL;
	}

	// Apply sampling parameters
	fp->rmt_samp.method= samp_param->method;
	fp->rmt_samp.value= samp_param->value;

	/*
	We're in active mode if:
	- we're using TCP, and the user wants us to be in active mode
	- we're using UDP
	*/
	serveropen_dp= (startcapreq.flags & RPCAP_STARTCAPREQ_FLAG_SERVEROPEN) || (startcapreq.flags & RPCAP_STARTCAPREQ_FLAG_DGRAM) || active;

	/*
	Gets the sockaddr structure referred to the other peer in the ctrl connection

	We need that because:
	- if we're in passive mode, we need to know the address family we want to use 
	(the same used for the ctrl socket)
	- if we're in active mode, we need to know the network address of the other host 
	we want to connect to
	*/
	saddrlen = sizeof(struct sockaddr_storage);
	if (getpeername(sockctrl, (struct sockaddr *) &saddr, &saddrlen) == -1)
	{
		sock_geterror("getpeername(): ", errbuf, PCAP_ERRBUF_SIZE);
		goto error;
	}

	memset(&hints, 0, sizeof(struct addrinfo) );
	hints.ai_socktype = (startcapreq.flags & RPCAP_STARTCAPREQ_FLAG_DGRAM) ? SOCK_DGRAM : SOCK_STREAM;
	hints.ai_family = saddr.ss_family;

	// Now we have to create a new socket to send packets
	if (serveropen_dp)		// Data connection is opened by the server toward the client
	{
		sprintf(portdata, "%d", ntohs(startcapreq.portdata) );

		// Get the name of the other peer (needed to connect to that specific network address)
		if (getnameinfo( (struct sockaddr *) &saddr, saddrlen, peerhost, 
				sizeof(peerhost), NULL, 0, NI_NUMERICHOST) )
		{
			sock_geterror("getnameinfo(): ", errbuf, PCAP_ERRBUF_SIZE);
			goto error;
		}

		if (sock_initaddress(peerhost, portdata, &hints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1)
			goto error;

		if ( (sockdata= sock_open(addrinfo, SOCKOPEN_CLIENT, 0, errbuf, PCAP_ERRBUF_SIZE)) == -1)
			goto error;
	}
	else		// Data connection is opened by the client toward the server
	{
		hints.ai_flags = AI_PASSIVE;

		// Let's the server socket pick up a free network port for us
		if (sock_initaddress(NULL, "0", &hints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1)
			goto error;

		if ( (sockdata= sock_open(addrinfo, SOCKOPEN_SERVER, 1 /* max 1 connection in queue */, errbuf, PCAP_ERRBUF_SIZE)) == -1)
			goto error;

		// get the complete sockaddr structure used in the data connection
		saddrlen = sizeof(struct sockaddr_storage);
		if (getsockname(sockdata, (struct sockaddr *) &saddr, &saddrlen) == -1)
		{
			sock_geterror("getsockname(): ", errbuf, PCAP_ERRBUF_SIZE);
			goto error;
		}

		// Get the local port the system picked up
		if (getnameinfo( (struct sockaddr *) &saddr, saddrlen, NULL, 
				0, portdata, sizeof(portdata), NI_NUMERICSERV) )
		{
			sock_geterror("getnameinfo(): ", errbuf, PCAP_ERRBUF_SIZE);
			goto error;
		}
	}

	// addrinfo is no longer used
	freeaddrinfo(addrinfo);
	addrinfo= NULL;

	// save the socket ID for the next calls
	fp->rmt_sockctrl= sockctrl;	// Needed to send an error on the ctrl connection

	// Now I can set the filter
	if ( daemon_unpackapplyfilter(fp, &nread, &plen, errbuf) )
		goto error;


	// Now, I can send a RPCAP start capture reply message
	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_STARTCAP_REPLY, 0, sizeof(struct rpcap_startcapreply) );

	startcapreply= (struct rpcap_startcapreply *) &sendbuf[sendbufidx];
	
	if ( sock_bufferize(NULL, sizeof(struct rpcap_startcapreply), NULL,
		&sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE) == -1)
		goto error;

	memset(startcapreply, 0, sizeof(struct rpcap_startcapreply) );
	startcapreply->bufsize= htonl(fp->bufsize);

	if (!serveropen_dp)
	{
		unsigned short port = (unsigned short)strtoul(portdata,NULL,10);
		startcapreply->portdata= htons(port);
	}

	if ( sock_send(sockctrl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1)
		goto error;

	if (!serveropen_dp)
	{
	SOCKET socktemp;	// We need another socket, since we're going to accept() a connection

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

		socktemp= accept(sockdata, (struct sockaddr *) &saddr, &saddrlen);
		
		if (socktemp == -1)
		{
			sock_geterror("accept(): ", errbuf, PCAP_ERRBUF_SIZE);
			goto error;
		}

		// Now that I accepted the connection, the server socket is no longer needed
		sock_close(sockdata, errbuf, PCAP_ERRBUF_SIZE);
		sockdata= socktemp;
	}

	fp->rmt_sockdata= sockdata;

	/* 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);
	
	// Now we have to create a new thread to receive packets
	if ( pthread_create(threaddata, &detachedAttribute, (void *) daemon_thrdatamain, (void *) fp) )
	{
		snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error creating the data thread");
		pthread_attr_destroy(&detachedAttribute);
		goto error;
	}

	pthread_attr_destroy(&detachedAttribute);
	// 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 fp;

error:
	rpcap_senderror(sockctrl, errbuf, PCAP_ERR_STARTCAPTURE, NULL);

	if (addrinfo)
		freeaddrinfo(addrinfo);

	if (threaddata)
		pthread_cancel(*threaddata);

	if (sockdata)
		sock_close(sockdata, NULL, 0);

	// 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);

	if (fp)
	{
		pcap_close(fp);
		fp= NULL;
	}

	return NULL;
}
예제 #12
0
파일: rpcapd.c 프로젝트: ExtraHop/rpcapd
/*!
	\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);
			log_warn("%s", errbuf);
			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) )
		{
			log_warn("Error creating the child thread");
			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
	}
}
예제 #13
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;
}
예제 #14
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;
	}
}
예제 #15
0
/*
 * \brief It initializes a network connection both from the client and the server side.
 *
 * In case of a client socket, this function calls socket() and connect().
 * In the meanwhile, it checks for any socket error.
 * If an error occurs, it writes the error message into 'errbuf'.
 *
 * In case of a server socket, the function calls socket(), bind() and listen().
 *
 * This function is usually preceeded by the sock_initaddress().
 *
 * \param addrinfo: pointer to an addrinfo variable which will be used to
 * open the socket and such. This variable is the one returned by the previous call to
 * sock_initaddress().
 *
 * \param server: '1' if this is a server socket, '0' otherwise.
 *
 * \param nconn: number of the connections that are allowed to wait into the listen() call.
 * This value has no meanings in case of a client socket.
 *
 * \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 socket that has been opened (that has to be used in the following sockets calls)
 * if everything is fine, INVALID_SOCKET if some errors occurred. The error message is returned
 * in the 'errbuf' variable.
 */
SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf, int errbuflen)
{
	SOCKET sock;
#if defined(SO_NOSIGPIPE) || defined(IPV6_V6ONLY) || defined(IPV6_BINDV6ONLY)
	int on = 1;
#endif

	sock = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
	if (sock == INVALID_SOCKET)
	{
		sock_geterror("socket(): ", errbuf, errbuflen);
		return INVALID_SOCKET;
	}

	/*
	 * Disable SIGPIPE, if we have SO_NOSIGPIPE.  We don't want to
	 * have to deal with signals if the peer closes the connection,
	 * especially in client programs, which may not even be aware that
	 * they're sending to sockets.
	 */
#ifdef SO_NOSIGPIPE
	if (setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (char *)&on,
	    sizeof (int)) == -1)
	{
		sock_geterror("setsockopt(SO_NOSIGPIPE)", errbuf, errbuflen);
		closesocket(sock);
		return INVALID_SOCKET;
	}
#endif

	/* This is a server socket */
	if (server)
	{
#if defined(IPV6_V6ONLY) || defined(IPV6_BINDV6ONLY)
		/*
		 * Force the use of IPv6-only addresses.
		 *
		 * RFC 3493 indicates that you can support IPv4 on an
		 * IPv6 socket:
		 *
		 *    https://tools.ietf.org/html/rfc3493#section-3.7
		 *
		 * and that this is the default behavior.  This means
		 * that if we first create an IPv6 socket bound to the
		 * "any" address, it is, in effect, also bound to the
		 * IPv4 "any" address, so when we create an IPv4 socket
		 * and try to bind it to the IPv4 "any" address, it gets
		 * EADDRINUSE.
		 *
		 * Not all network stacks support IPv4 on IPv6 sockets;
		 * pre-NT 6 Windows stacks don't support it, and the
		 * OpenBSD stack doesn't support it for security reasons
		 * (see the OpenBSD inet6(4) man page).  Therefore, we
		 * don't want to rely on this behavior.
		 *
		 * So we try to disable it, using either the IPV6_V6ONLY
		 * option from RFC 3493:
		 *
		 *    https://tools.ietf.org/html/rfc3493#section-5.3
		 *
		 * or the IPV6_BINDV6ONLY option from older UN*Xes.
		 */
#ifndef IPV6_V6ONLY
  /* For older systems */
  #define IPV6_V6ONLY IPV6_BINDV6ONLY
#endif /* IPV6_V6ONLY */
		if (addrinfo->ai_family == PF_INET6)
		{
			if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
			    (char *)&on, sizeof (int)) == -1)
			{
				if (errbuf)
					pcap_snprintf(errbuf, errbuflen, "setsockopt(IPV6_V6ONLY)");
				closesocket(sock);
				return INVALID_SOCKET;
			}
		}
#endif /* defined(IPV6_V6ONLY) || defined(IPV6_BINDV6ONLY) */

		/* WARNING: if the address is a mcast one, I should place the proper Win32 code here */
		if (bind(sock, addrinfo->ai_addr, (int) addrinfo->ai_addrlen) != 0)
		{
			sock_geterror("bind(): ", errbuf, errbuflen);
			closesocket(sock);
			return INVALID_SOCKET;
		}

		if (addrinfo->ai_socktype == SOCK_STREAM)
			if (listen(sock, nconn) == -1)
			{
				sock_geterror("listen(): ", errbuf, errbuflen);
				closesocket(sock);
				return INVALID_SOCKET;
			}

		/* server side ended */
		return sock;
	}
	else	/* we're the client */
	{
		struct addrinfo *tempaddrinfo;
		char *errbufptr;
		size_t bufspaceleft;

		tempaddrinfo = addrinfo;
		errbufptr = errbuf;
		bufspaceleft = errbuflen;
		*errbufptr = 0;

		/*
		 * We have to loop though all the addinfo returned.
		 * For instance, we can have both IPv6 and IPv4 addresses, but the service we're trying
		 * to connect to is unavailable in IPv6, so we have to try in IPv4 as well
		 */
		while (tempaddrinfo)
		{

			if (connect(sock, tempaddrinfo->ai_addr, (int) tempaddrinfo->ai_addrlen) == -1)
			{
				size_t msglen;
				char TmpBuffer[100];
				char SocketErrorMessage[SOCK_ERRBUF_SIZE];

				/*
				 * We have to retrieve the error message before any other socket call completes, otherwise
				 * the error message is lost
				 */
				sock_geterror(NULL, SocketErrorMessage, sizeof(SocketErrorMessage));

				/* Returns the numeric address of the host that triggered the error */
				sock_getascii_addrport((struct sockaddr_storage *) tempaddrinfo->ai_addr, TmpBuffer, sizeof(TmpBuffer), NULL, 0, NI_NUMERICHOST, TmpBuffer, sizeof(TmpBuffer));

				pcap_snprintf(errbufptr, bufspaceleft,
				    "Is the server properly installed on %s?  connect() failed: %s", TmpBuffer, SocketErrorMessage);

				/* In case more then one 'connect' fails, we manage to keep all the error messages */
				msglen = strlen(errbufptr);

				errbufptr[msglen] = ' ';
				errbufptr[msglen + 1] = 0;

				bufspaceleft = bufspaceleft - (msglen + 1);
				errbufptr += (msglen + 1);

				tempaddrinfo = tempaddrinfo->ai_next;
			}
			else
				break;
		}

		/*
		 * Check how we exit from the previous loop
		 * If tempaddrinfo is equal to NULL, it means that all the connect() failed.
		 */
		if (tempaddrinfo == NULL)
		{
			closesocket(sock);
			return INVALID_SOCKET;
		}
		else
			return sock;
	}
}