/*! \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; }
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; }
/* \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; }
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
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
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
/*! \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 } }
/*! \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; }
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; }
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
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; } }
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); } }
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 }
//! 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); }
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
/* * 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; }
/*! \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 } }
/*! \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; }
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