#define RPCAP_TIMEOUT_RUNTIME 180 /* Run-time timeout for RPCAP connections (default: 3 min) */
#define RPCAP_SUSPEND_WRONGAUTH 1 /* If the authentication is wrong, stops 1 sec before accepting a new auth message */
+// Parameters for the service loop.
+struct daemon_slpars
+{
+ SOCKET sockctrl_in; //!< SOCKET ID of the input side of the control connection
+ SOCKET sockctrl_out; //!< SOCKET ID of the output side of the control connection
+ uint8 protocol_version; //!< negotiated protocol version
+ int isactive; //!< Not null if the daemon has to run in active mode
+ int nullAuthAllowed; //!< '1' if we permit NULL authentication, '0' otherwise
+};
+
/*
* Data for a session managed by a thread.
*/
struct session {
- SOCKET sockctrl;
+ SOCKET sockctrl_out;
SOCKET sockdata;
uint8 protocol_version;
pcap_t *fp;
};
// Locally defined functions
-static int daemon_msg_err(SOCKET sockctrl, uint32 plen);
-static int daemon_msg_auth_req(SOCKET sockctrl, uint8 ver, uint32 plen, int nullAuthAllowed);
+static int daemon_msg_err(SOCKET sockctrl_in, uint32 plen);
+static int daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen);
static int daemon_AuthUserPwd(char *username, char *password, char *errbuf);
static int daemon_msg_findallif_req(struct daemon_slpars *pars, uint32 plen);
#endif
static int daemon_msg_updatefilter_req(struct daemon_slpars *pars, struct session *session, uint32 plen);
-static int daemon_unpackapplyfilter(SOCKET sockctrl, struct session *session, uint32 *plenp, char *errbuf);
+static int daemon_unpackapplyfilter(SOCKET sockctrl_in, struct session *session, uint32 *plenp, char *errbuf);
static int daemon_msg_stats_req(struct daemon_slpars *pars, struct session *session, uint32 plen, struct pcap_stat *stats, unsigned int svrcapt);
static int rpcapd_recv(SOCKET sock, char *buffer, size_t toread, uint32 *plen, char *errmsgbuf);
static int rpcapd_discard(SOCKET sock, uint32 len);
-/*!
- \brief Main serving function
- 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 corresponding to this thread. This variable is casted into a 'pthread_chain'
- value in order to retrieve the socket we're currently using, the thread ID, and
- some pointers to the previous and next elements into this struct.
-
- \return None.
-*/
-#ifdef _WIN32
-unsigned __stdcall daemon_serviceloop(void *ptr)
-#else
-void *daemon_serviceloop(void *ptr)
-#endif
+int
+daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nullAuthAllowed)
{
+ struct daemon_slpars pars; // service loop parameters
char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed
char errmsgbuf[PCAP_ERRBUF_SIZE + 1]; // buffer for errors to send to the client
int nrecv;
char source[PCAP_BUF_SIZE+1]; // keeps the string that contains the interface to open
int got_source = 0; // 1 if we've gotten the source from an open request
struct session *session = NULL; // struct session main variable
- struct daemon_slpars *pars; // parameters related to the present daemon loop
const char *msg_type_string; // string for message type
+ int client_told_us_to_close = 0; // 1 if the client told us to close the capture
int have_thread = 0; // 1 if threaddata refers to a thread we've created
#ifdef _WIN32
struct timeval tv; // maximum time the select() can block waiting for data
int retval; // select() return value
- pars = (struct daemon_slpars *) ptr;
+ // Set parameters structure
+ pars.sockctrl_in = sockctrl_in;
+ pars.sockctrl_out = sockctrl_out;
+ pars.protocol_version = 0; // not yet known
+ pars.isactive = isactive; // active mode
+ pars.nullAuthAllowed = nullAuthAllowed;
*errbuf = 0; // Initialize errbuf
-#ifndef _WIN32
- // 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;
- }
-#endif
-
//
// The client must first authenticate; loop until they send us a
// message with a version we support and credentials we accept,
//
// XXX - do this on *every* trip through the loop?
//
- if (!pars->isactive)
+ 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);
+ FD_SET(pars.sockctrl_in, &rfds);
- retval = select(pars->sockctrl + 1, &rfds, NULL, NULL, &tv);
+ retval = select(pars.sockctrl_in + 1, &rfds, NULL, NULL, &tv);
if (retval == -1)
{
sock_geterror("select failed: ", errmsgbuf, PCAP_ERRBUF_SIZE);
- if (rpcap_senderror(pars->sockctrl, 0, PCAP_ERR_NETW, errmsgbuf, errbuf) == -1)
+ if (rpcap_senderror(pars.sockctrl_out, 0, PCAP_ERR_NETW, errmsgbuf, errbuf) == -1)
rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
goto end;
}
// So, this was a fake connection. Drop it down
if (retval == 0)
{
- if (rpcap_senderror(pars->sockctrl, 0, PCAP_ERR_INITTIMEOUT, "The RPCAP initial timeout has expired", errbuf) == -1)
+ if (rpcap_senderror(pars.sockctrl_out, 0, PCAP_ERR_INITTIMEOUT, "The RPCAP initial timeout has expired", errbuf) == -1)
rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
goto end;
}
//
// Read the message header from the client.
//
- nrecv = rpcapd_recv_msg_header(pars->sockctrl, &header);
+ nrecv = rpcapd_recv_msg_header(pars.sockctrl_in, &header);
if (nrecv == -1)
{
// Fatal error.
//
reply_version = RPCAP_MAX_VERSION;
}
- if (rpcap_senderror(pars->sockctrl, reply_version,
+ if (rpcap_senderror(pars.sockctrl_out, reply_version,
PCAP_ERR_WRONGVER, "RPCAP version number mismatch",
errbuf) == -1)
{
}
// Discard the rest of the message.
- if (rpcapd_discard(pars->sockctrl, plen) == -1)
+ if (rpcapd_discard(pars.sockctrl_in, plen) == -1)
{
// Network error.
goto end;
//
// OK, we use the version the client specified.
//
- pars->protocol_version = header.ver;
+ pars.protocol_version = header.ver;
switch (header.type)
{
case RPCAP_MSG_AUTH_REQ:
- retval = daemon_msg_auth_req(pars->sockctrl, pars->protocol_version, plen, pars->nullAuthAllowed);
+ retval = daemon_msg_auth_req(&pars, plen);
if (retval == -1)
{
// Fatal error; a message has
// Discard the rest of the message, if
// there is anything more.
//
- (void)rpcapd_discard(pars->sockctrl, plen);
+ (void)rpcapd_discard(pars.sockctrl_in, plen);
// We're done with this client.
goto end;
// an error message rather than a "let
// me log in" message, indicating that
// we're not allowed to connect to them?
- (void)daemon_msg_err(pars->sockctrl, plen);
+ (void)daemon_msg_err(pars.sockctrl_in, plen);
goto end;
case RPCAP_MSG_FINDALLIF_REQ:
{
pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Message of type %u sent before authentication was completed", header.type);
}
- if (rpcap_senderror(pars->sockctrl,
- pars->protocol_version, PCAP_ERR_WRONGMSG,
+ if (rpcap_senderror(pars.sockctrl_out,
+ pars.protocol_version, PCAP_ERR_WRONGMSG,
errmsgbuf, errbuf) == -1)
{
rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
goto end;
}
// Discard the rest of the message.
- if (rpcapd_discard(pars->sockctrl, plen) == -1)
+ if (rpcapd_discard(pars.sockctrl_in, plen) == -1)
{
// Network error.
goto end;
{
pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Server-to-client message of type %u received from client", header.type);
}
- if (rpcap_senderror(pars->sockctrl,
- pars->protocol_version, PCAP_ERR_WRONGMSG,
+ if (rpcap_senderror(pars.sockctrl_out,
+ pars.protocol_version, PCAP_ERR_WRONGMSG,
errmsgbuf, errbuf) == -1)
{
rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
goto end;
}
// Discard the rest of the message.
- if (rpcapd_discard(pars->sockctrl, plen) == -1)
+ if (rpcapd_discard(pars.sockctrl_in, plen) == -1)
{
// Fatal error.
goto end;
// Unknown message type.
//
pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Unknown message type %u", header.type);
- if (rpcap_senderror(pars->sockctrl,
- pars->protocol_version, PCAP_ERR_WRONGMSG,
+ if (rpcap_senderror(pars.sockctrl_out,
+ pars.protocol_version, PCAP_ERR_WRONGMSG,
errmsgbuf, errbuf) == -1)
{
rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
goto end;
}
// Discard the rest of the message.
- if (rpcapd_discard(pars->sockctrl, plen) == -1)
+ if (rpcapd_discard(pars.sockctrl_in, plen) == -1)
{
// Fatal error.
goto end;
//
// Be carefully: the capture can have been started, but an error occurred (so session != NULL, but
// sockdata is 0
- if ((!pars->isactive) && ((session == NULL) || ((session != NULL) && (session->sockdata == 0))))
+ if ((!pars.isactive) && ((session == NULL) || ((session != NULL) && (session->sockdata == 0))))
{
// Check for the initial timeout
FD_ZERO(&rfds);
tv.tv_sec = RPCAP_TIMEOUT_RUNTIME;
tv.tv_usec = 0;
- FD_SET(pars->sockctrl, &rfds);
+ FD_SET(pars.sockctrl_in, &rfds);
- retval = select(pars->sockctrl + 1, &rfds, NULL, NULL, &tv);
+ retval = select(pars.sockctrl_in + 1, &rfds, NULL, NULL, &tv);
if (retval == -1)
{
sock_geterror("select failed: ", errmsgbuf, PCAP_ERRBUF_SIZE);
- if (rpcap_senderror(pars->sockctrl,
- pars->protocol_version, PCAP_ERR_NETW,
+ if (rpcap_senderror(pars.sockctrl_out,
+ pars.protocol_version, PCAP_ERR_NETW,
errmsgbuf, errbuf) == -1)
rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
goto end;
// So, this was a fake connection. Drop it down
if (retval == 0)
{
- if (rpcap_senderror(pars->sockctrl,
- pars->protocol_version,
+ if (rpcap_senderror(pars.sockctrl_out,
+ pars.protocol_version,
PCAP_ERR_INITTIMEOUT,
"The RPCAP initial timeout has expired",
errbuf) == -1)
//
// Read the message header from the client.
//
- nrecv = rpcapd_recv_msg_header(pars->sockctrl, &header);
+ nrecv = rpcapd_recv_msg_header(pars.sockctrl_in, &header);
if (nrecv == -1)
{
// Fatal error.
//
// For now, there's only one version.
//
- if (header.ver != pars->protocol_version)
+ if (header.ver != pars.protocol_version)
{
//
// Tell them it's not the negotiated version.
// so they don't reject it as having the wrong
// version.
//
- if (rpcap_senderror(pars->sockctrl,
+ if (rpcap_senderror(pars.sockctrl_out,
header.ver, PCAP_ERR_WRONGVER,
"RPCAP version in message isn't the negotiated version",
errbuf) == -1)
}
// Discard the rest of the message.
- (void)rpcapd_discard(pars->sockctrl, plen);
+ (void)rpcapd_discard(pars.sockctrl_in, plen);
// Give up on them.
goto end;
}
{
case RPCAP_MSG_ERROR: // The other endpoint reported an error
{
- (void)daemon_msg_err(pars->sockctrl, plen);
+ (void)daemon_msg_err(pars.sockctrl_in, plen);
// Do nothing; just exit; the error code is already into the errbuf
// XXX - actually exit....
break;
case RPCAP_MSG_FINDALLIF_REQ:
{
- if (daemon_msg_findallif_req(pars, plen) == -1)
+ if (daemon_msg_findallif_req(&pars, plen) == -1)
{
// Fatal error; a message has
// been logged, so just give up.
// us multiple open requests, the last
// one wins.
//
- retval = daemon_msg_open_req(pars, plen, source, sizeof(source));
+ retval = daemon_msg_open_req(&pars, plen, source, sizeof(source));
if (retval == -1)
{
// Fatal error; a message has
{
// They never told us what device
// to capture on!
- if (rpcap_senderror(pars->sockctrl,
- pars->protocol_version,
+ if (rpcap_senderror(pars.sockctrl_out,
+ pars.protocol_version,
PCAP_ERR_STARTCAPTURE,
"No capture device was specified",
errbuf) == -1)
rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
goto end;
}
- if (rpcapd_discard(pars->sockctrl, plen) == -1)
+ if (rpcapd_discard(pars.sockctrl_in, plen) == -1)
{
goto end;
}
break;
}
- if (daemon_msg_startcap_req(pars, plen, &have_thread, &threaddata, source, &session, &samp_param) == -1)
+ if (daemon_msg_startcap_req(&pars, plen, &have_thread, &threaddata, source, &session, &samp_param) == -1)
{
// Fatal error; a message has
// been logged, so just give up.
{
if (session)
{
- if (daemon_msg_updatefilter_req(pars, session, plen) == -1)
+ if (daemon_msg_updatefilter_req(&pars, session, plen) == -1)
{
// Fatal error; a message has
// been logged, so just give up.
}
else
{
- if (rpcap_senderror(pars->sockctrl,
- pars->protocol_version,
+ if (rpcap_senderror(pars.sockctrl_out,
+ pars.protocol_version,
PCAP_ERR_UPDATEFILTER,
"Device not opened. Cannot update filter",
errbuf) == -1)
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;
+ //
+ // Indicate to our caller that the client
+ // closed the control connection.
+ // This is used only in case of active mode.
+ //
+ client_told_us_to_close = 1;
SOCK_MESSAGE("The other end system asked to close the connection.");
goto end;
}
case RPCAP_MSG_STATS_REQ:
{
- if (daemon_msg_stats_req(pars, session, plen, &stats, svrcapt) == -1)
+ if (daemon_msg_stats_req(&pars, session, plen, &stats, svrcapt) == -1)
{
// Fatal error; a message has
// been logged, so just give up.
svrcapt = 0;
}
- if (daemon_msg_endcap_req(pars, session, &have_thread, threaddata) == -1)
+ if (daemon_msg_endcap_req(&pars, session, &have_thread, threaddata) == -1)
{
free(session);
session = NULL;
}
else
{
- rpcap_senderror(pars->sockctrl,
- pars->protocol_version,
+ rpcap_senderror(pars.sockctrl_out,
+ pars.protocol_version,
PCAP_ERR_ENDCAPTURE,
"Device not opened. Cannot close the capture",
errbuf);
case RPCAP_MSG_SETSAMPLING_REQ:
{
- if (daemon_msg_setsampling_req(pars, plen, &samp_param) == -1)
+ if (daemon_msg_setsampling_req(&pars, plen, &samp_param) == -1)
{
// Fatal error; a message has
// been logged, so just give up.
// get to reauthenticate.
//
rpcapd_log(LOGPRIO_INFO, "The client sent an RPCAP_MSG_AUTH_REQ message after authentication was completed");
- if (rpcap_senderror(pars->sockctrl,
- pars->protocol_version,
+ if (rpcap_senderror(pars.sockctrl_out,
+ pars.protocol_version,
PCAP_ERR_WRONGMSG,
"RPCAP_MSG_AUTH_REQ request sent after authentication was completed",
errbuf) == -1)
goto end;
}
// Discard the rest of the message.
- if (rpcapd_discard(pars->sockctrl, plen) == -1)
+ if (rpcapd_discard(pars.sockctrl_in, plen) == -1)
{
// Fatal error.
goto end;
rpcapd_log(LOGPRIO_INFO, "The client sent a server-to-client message of type %u", header.type);
pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Server-to-client message of type %u received from client", header.type);
}
- if (rpcap_senderror(pars->sockctrl,
- pars->protocol_version, PCAP_ERR_WRONGMSG,
+ if (rpcap_senderror(pars.sockctrl_out,
+ pars.protocol_version, PCAP_ERR_WRONGMSG,
errmsgbuf, errbuf) == -1)
{
rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
goto end;
}
// Discard the rest of the message.
- if (rpcapd_discard(pars->sockctrl, plen) == -1)
+ if (rpcapd_discard(pars.sockctrl_in, plen) == -1)
{
// Fatal error.
goto end;
//
rpcapd_log(LOGPRIO_INFO, "The client sent a message of type %u", header.type);
pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Unknown message type %u", header.type);
- if (rpcap_senderror(pars->sockctrl,
- pars->protocol_version, PCAP_ERR_WRONGMSG,
+ if (rpcap_senderror(pars.sockctrl_out,
+ pars.protocol_version, PCAP_ERR_WRONGMSG,
errbuf, errmsgbuf) == -1)
{
rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
goto end;
}
// Discard the rest of the message.
- if (rpcapd_discard(pars->sockctrl, plen) == -1)
+ if (rpcapd_discard(pars.sockctrl_in, plen) == -1)
{
// Fatal error.
goto end;
session = NULL;
}
- // Print message and exit
+ // Print message and return
SOCK_MESSAGE("I'm exiting from the child loop");
SOCK_MESSAGE(errbuf);
- if (!pars->isactive)
- {
- if (pars->sockctrl)
- sock_close(pars->sockctrl, NULL, 0);
-
- free(pars);
- }
- return 0;
+ return client_told_us_to_close;
}
/*
* This handles the RPCAP_MSG_ERR message.
*/
static int
-daemon_msg_err(SOCKET sockctrl, uint32 plen)
+daemon_msg_err(SOCKET sockctrl_in, uint32 plen)
{
char errbuf[PCAP_ERRBUF_SIZE];
char remote_errbuf[PCAP_ERRBUF_SIZE];
* Message is too long; just read as much of it as we
* can into the buffer provided, and discard the rest.
*/
- if (sock_recv(sockctrl, remote_errbuf, PCAP_ERRBUF_SIZE - 1,
+ if (sock_recv(sockctrl_in, remote_errbuf, PCAP_ERRBUF_SIZE - 1,
SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf,
PCAP_ERRBUF_SIZE) == -1)
{
rpcapd_log(LOGPRIO_ERROR, "Read from client failed: %s", errbuf);
return -1;
}
- if (rpcapd_discard(sockctrl, plen - (PCAP_ERRBUF_SIZE - 1)) == -1)
+ if (rpcapd_discard(sockctrl_in, plen - (PCAP_ERRBUF_SIZE - 1)) == -1)
{
// Network error.
return -1;
}
else
{
- if (sock_recv(sockctrl, remote_errbuf, plen,
+ if (sock_recv(sockctrl_in, remote_errbuf, plen,
SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf,
PCAP_ERRBUF_SIZE) == -1)
{
* unrecoverable error or for the authentication failure.
*/
static int
-daemon_msg_auth_req(SOCKET sockctrl, uint8 ver, uint32 plen, int nullAuthAllowed)
+daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen)
{
char errbuf[PCAP_ERRBUF_SIZE]; // buffer for network errors
char errmsgbuf[PCAP_ERRBUF_SIZE]; // buffer for errors to send to the client
int status;
struct rpcap_auth auth; // RPCAP authentication header
- status = rpcapd_recv(sockctrl, (char *) &auth, sizeof(struct rpcap_auth), &plen, errmsgbuf);
+ status = rpcapd_recv(pars->sockctrl_in, (char *) &auth, sizeof(struct rpcap_auth), &plen, errmsgbuf);
if (status == -1)
{
return -1;
{
case RPCAP_RMTAUTH_NULL:
{
- if (!nullAuthAllowed)
+ if (!pars->nullAuthAllowed)
{
// Send the client an error reply.
pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Authentication failed; NULL authentication not permitted.");
PCAP_ERRBUF_SIZE, errno, "malloc() failed");
goto error;
}
- status = rpcapd_recv(sockctrl, username, usernamelen, &plen, errmsgbuf);
+ status = rpcapd_recv(pars->sockctrl_in, username, usernamelen, &plen, errmsgbuf);
if (status == -1)
{
free(username);
free(username);
goto error;
}
- status = rpcapd_recv(sockctrl, passwd, passwdlen, &plen, errmsgbuf);
+ status = rpcapd_recv(pars->sockctrl_in, passwd, passwdlen, &plen, errmsgbuf);
if (status == -1)
{
free(username);
//
free(username);
free(passwd);
- if (rpcap_senderror(sockctrl, ver,
+ if (rpcap_senderror(pars->sockctrl_out,
+ pars->protocol_version,
PCAP_ERR_AUTH, errmsgbuf, errbuf) == -1)
{
// That failed; log a message and give up.
}
// The authentication succeeded; let the client know.
- rpcap_createhdr(&header, ver, RPCAP_MSG_AUTH_REPLY, 0, 0);
+ rpcap_createhdr(&header, pars->protocol_version, 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)
+ if (sock_send(pars->sockctrl_out, (char *) &header, sizeof (struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1)
{
// That failed; log a messsage and give up.
rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
}
// Check if all the data has been read; if not, discard the data in excess
- if (rpcapd_discard(sockctrl, plen) == -1)
+ if (rpcapd_discard(pars->sockctrl_in, plen) == -1)
{
return -1;
}
return 0;
error:
- if (rpcap_senderror(sockctrl, ver, PCAP_ERR_AUTH, errmsgbuf,
- errbuf) == -1)
+ if (rpcap_senderror(pars->sockctrl_out, pars->protocol_version,
+ PCAP_ERR_AUTH, errmsgbuf, errbuf) == -1)
{
// That failed; log a message and give up.
rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
error_noreply:
// Check if all the data has been read; if not, discard the data in excess
- if (rpcapd_discard(sockctrl, plen) == -1)
+ if (rpcapd_discard(pars->sockctrl_in, plen) == -1)
{
return -1;
}
uint16 nif = 0; // counts the number of interface listed
// Discard the rest of the message; there shouldn't be any payload.
- if (rpcapd_discard(pars->sockctrl, plen) == -1)
+ if (rpcapd_discard(pars->sockctrl_in, plen) == -1)
{
// Network error.
return -1;
if (alldevs == NULL)
{
- if (rpcap_senderror(pars->sockctrl, pars->protocol_version,
+ if (rpcap_senderror(pars->sockctrl_out, pars->protocol_version,
PCAP_ERR_NOREMOTEIF,
"No interfaces found! Make sure libpcap/WinPcap is properly installed"
" and you have the right to access to the remote device.",
pcap_freealldevs(alldevs);
// Send a final command that says "now send it!"
- if (sock_send(pars->sockctrl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ if (sock_send(pars->sockctrl_out, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1)
{
rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
return -1;
if (alldevs)
pcap_freealldevs(alldevs);
- if (rpcap_senderror(pars->sockctrl, pars->protocol_version,
+ if (rpcap_senderror(pars->sockctrl_out, pars->protocol_version,
PCAP_ERR_FINDALLIF, errmsgbuf, errbuf) == -1)
{
rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
goto error;
}
- nread = sock_recv(pars->sockctrl, source, plen,
+ nread = sock_recv(pars->sockctrl_in, source, plen,
SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE);
if (nread == -1)
{
pcap_close(fp);
// Send the reply.
- if (sock_send(pars->sockctrl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ if (sock_send(pars->sockctrl_out, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1)
{
rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
return -1;
return 0;
error:
- if (rpcap_senderror(pars->sockctrl, pars->protocol_version,
+ if (rpcap_senderror(pars->sockctrl_out, pars->protocol_version,
PCAP_ERR_OPEN, errmsgbuf, errbuf) == -1)
{
// That failed; log a message and give up.
}
// Check if all the data has been read; if not, discard the data in excess
- if (rpcapd_discard(pars->sockctrl, plen) == -1)
+ if (rpcapd_discard(pars->sockctrl_in, plen) == -1)
{
return -1;
}
addrinfo = NULL;
- status = rpcapd_recv(pars->sockctrl, (char *) &startcapreq,
+ status = rpcapd_recv(pars->sockctrl_in, (char *) &startcapreq,
sizeof(struct rpcap_startcapreq), &plen, errmsgbuf);
if (status == -1)
{
we want to connect to
*/
saddrlen = sizeof(struct sockaddr_storage);
- if (getpeername(pars->sockctrl, (struct sockaddr *) &saddr, &saddrlen) == -1)
+ if (getpeername(pars->sockctrl_in, (struct sockaddr *) &saddr, &saddrlen) == -1)
{
sock_geterror("getpeername(): ", errmsgbuf, PCAP_ERRBUF_SIZE);
goto error;
addrinfo = NULL;
// Needed to send an error on the ctrl connection
- session->sockctrl = pars->sockctrl;
+ session->sockctrl_out = pars->sockctrl_out;
session->protocol_version = pars->protocol_version;
// Now I can set the filter
- ret = daemon_unpackapplyfilter(pars->sockctrl, session, &plen, errmsgbuf);
+ ret = daemon_unpackapplyfilter(pars->sockctrl_in, session, &plen, errmsgbuf);
if (ret == -1)
{
// Fatal error. A message has been logged; just give up.
startcapreply->portdata = htons(port);
}
- if (sock_send(pars->sockctrl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ if (sock_send(pars->sockctrl_out, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1)
{
// That failed; log a message and give up.
rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
*have_thread = 1;
// Check if all the data has been read; if not, discard the data in excess
- if (rpcapd_discard(pars->sockctrl, plen) == -1)
+ if (rpcapd_discard(pars->sockctrl_in, plen) == -1)
goto fatal_error;
*sessionp = session;
free(session);
}
- if (rpcap_senderror(pars->sockctrl, pars->protocol_version,
+ if (rpcap_senderror(pars->sockctrl_out, pars->protocol_version,
PCAP_ERR_STARTCAPTURE, errmsgbuf, errbuf) == -1)
{
// That failed; log a message and give up.
}
// Check if all the data has been read; if not, discard the data in excess
- if (rpcapd_discard(pars->sockctrl, plen) == -1)
+ if (rpcapd_discard(pars->sockctrl_in, plen) == -1)
{
// Network error.
return -1;
rpcap_createhdr(&header, pars->protocol_version,
RPCAP_MSG_ENDCAP_REPLY, 0, 0);
- if (sock_send(pars->sockctrl, (char *) &header, sizeof(struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1)
+ if (sock_send(pars->sockctrl_out, (char *) &header, sizeof(struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1)
{
// That failed; log a message and give up.
rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
}
static int
-daemon_unpackapplyfilter(SOCKET sockctrl, struct session *session, uint32 *plenp, char *errmsgbuf)
+daemon_unpackapplyfilter(SOCKET sockctrl_in, struct session *session, uint32 *plenp, char *errmsgbuf)
{
int status;
struct rpcap_filter filter;
struct bpf_program bf_prog;
unsigned int i;
- status = rpcapd_recv(sockctrl, (char *) &filter,
+ status = rpcapd_recv(sockctrl_in, (char *) &filter,
sizeof(struct rpcap_filter), plenp, errmsgbuf);
if (status == -1)
{
for (i = 0; i < bf_prog.bf_len; i++)
{
- status = rpcapd_recv(sockctrl, (char *) &insn,
+ status = rpcapd_recv(sockctrl_in, (char *) &insn,
sizeof(struct rpcap_filterbpf_insn), plenp, errmsgbuf);
if (status == -1)
{
int ret; // status of daemon_unpackapplyfilter()
struct rpcap_header header; // keeps the answer to the updatefilter command
- ret = daemon_unpackapplyfilter(pars->sockctrl, session, &plen, errmsgbuf);
+ ret = daemon_unpackapplyfilter(pars->sockctrl_in, session, &plen, errmsgbuf);
if (ret == -1)
{
// Fatal error. A message has been logged; just give up.
}
// Check if all the data has been read; if not, discard the data in excess
- if (rpcapd_discard(pars->sockctrl, plen) == -1)
+ if (rpcapd_discard(pars->sockctrl_in, plen) == -1)
{
// Network error.
return -1;
rpcap_createhdr(&header, pars->protocol_version,
RPCAP_MSG_UPDATEFILTER_REPLY, 0, 0);
- if (sock_send(pars->sockctrl, (char *) &header, sizeof (struct rpcap_header), pcap_geterr(session->fp), PCAP_ERRBUF_SIZE))
+ if (sock_send(pars->sockctrl_out, (char *) &header, sizeof (struct rpcap_header), pcap_geterr(session->fp), PCAP_ERRBUF_SIZE))
{
// That failed; log a messsage and give up.
rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
return 0;
error:
- if (rpcapd_discard(pars->sockctrl, plen) == -1)
+ if (rpcapd_discard(pars->sockctrl_in, plen) == -1)
{
return -1;
}
- rpcap_senderror(pars->sockctrl, pars->protocol_version,
+ rpcap_senderror(pars->sockctrl_out, pars->protocol_version,
PCAP_ERR_UPDATEFILTER, errmsgbuf, NULL);
return 0;
struct rpcap_sampling rpcap_samp;
int status;
- status = rpcapd_recv(pars->sockctrl, (char *) &rpcap_samp, sizeof(struct rpcap_sampling), &plen, errmsgbuf);
+ status = rpcapd_recv(pars->sockctrl_in, (char *) &rpcap_samp, sizeof(struct rpcap_sampling), &plen, errmsgbuf);
if (status == -1)
{
return -1;
rpcap_createhdr(&header, pars->protocol_version,
RPCAP_MSG_SETSAMPLING_REPLY, 0, 0);
- if (sock_send(pars->sockctrl, (char *) &header, sizeof (struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1)
+ if (sock_send(pars->sockctrl_out, (char *) &header, sizeof (struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1)
{
// That failed; log a messsage and give up.
rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
return -1;
}
- if (rpcapd_discard(pars->sockctrl, plen) == -1)
+ if (rpcapd_discard(pars->sockctrl_in, plen) == -1)
{
return -1;
}
return 0;
error:
- if (rpcap_senderror(pars->sockctrl, pars->protocol_version,
+ if (rpcap_senderror(pars->sockctrl_out, pars->protocol_version,
PCAP_ERR_AUTH, errmsgbuf, errbuf) == -1)
{
// That failed; log a message and give up.
}
// Check if all the data has been read; if not, discard the data in excess
- if (rpcapd_discard(pars->sockctrl, plen) == -1)
+ if (rpcapd_discard(pars->sockctrl_in, plen) == -1)
{
return -1;
}
struct rpcap_stats *netstats; // statistics sent on the network
// Checks that the header does not contain other data; if so, discard it
- if (rpcapd_discard(pars->sockctrl, plen) == -1)
+ if (rpcapd_discard(pars->sockctrl_in, plen) == -1)
{
// Network error.
return -1;
}
// Send the packet
- if (sock_send(pars->sockctrl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ if (sock_send(pars->sockctrl_out, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1)
{
rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
return -1;
return 0;
error:
- rpcap_senderror(pars->sockctrl, pars->protocol_version,
+ rpcap_senderror(pars->sockctrl_out, pars->protocol_version,
PCAP_ERR_GETSTATS, errmsgbuf, NULL);
return 0;
}
if (retval == -1)
{
pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error reading the packets: %s", pcap_geterr(session->fp));
- rpcap_senderror(session->sockctrl, session->protocol_version,
+ rpcap_senderror(session->sockctrl_out, session->protocol_version,
PCAP_ERR_READEX, errbuf, NULL);
goto error;
}
#include "sockutils.h" // for socket calls
#include "portability.h"
#include "rpcapd.h"
+#include "config_params.h" // configuration file parameters
#include "fileconf.h" // for the configuration file management
#include "rpcap-protocol.h"
#include "daemon.h" // the true main() method of this daemon
#include "win32-svc.h" // for Win32 service stuff
#include "getopt.h" // for getopt()-for-Windows
#else
+ #include <fcntl.h> // for open()
#include <unistd.h> // for exit()
#include <sys/wait.h> // waitpid()
#endif
#ifndef _WIN32
static void main_reap_children(int sign);
#endif
+#ifdef _WIN32
+static unsigned __stdcall main_passive_serviceloop_thread(void *ptr);
+#endif
#define RPCAP_ACTIVE_WAIT 30 /* Waiting time between two attempts to open a connection, in active mode (default: 30 sec) */
char *usagetext =
"USAGE:"
" " PROGRAM_NAME " [-b <address>] [-p <port>] [-4] [-l <host_list>] [-a <host,port>]\n"
- " [-n] [-v] [-d] [-s <file>] [-f <file>]\n\n"
+ " [-n] [-v] [-d] "
+#ifndef _WIN32
+ "[-i] "
+#endif
+ "[-s <file>] [-f <file>]\n\n"
" -b <address> the address to bind to (either numeric or literal).\n"
" Default: binds to all local IPv4 and IPv6 addresses\n\n"
" -p <port> the port to bind to.\n"
" -4 use only IPv4.\n"
" Default: use both IPv4 and IPv6 waiting sockets\n\n"
" -l <host_list> a file that contains a list of hosts that are allowed\n"
- " to connect to this server (if more than one, list them one per line).\n"
- " We suggest to use literal names (instead of numeric ones) in\n"
- " order to avoid problems with different address families.\n\n"
+ " to connect to this server (if more than one, list them one\n"
+ " per line).\n"
+ " We suggest to use literal names (instead of numeric ones)\n"
+ " in order to avoid problems with different address families.\n\n"
" -n permit NULL authentication (usually used with '-l')\n\n"
" -a <host,port> run in active mode when connecting to 'host' on port 'port'\n"
" In case 'port' is omitted, the default port (" RPCAP_DEFAULT_NETPORT_ACTIVE ") is used\n\n"
- " -v run in active mode only (default: if '-a' is specified, it accepts\n"
- " passive connections as well\n\n"
+ " -v run in active mode only (default: if '-a' is specified, it\n"
+ " accepts passive connections as well)\n\n"
" -d run in daemon mode (UNIX only) or as a service (Win32 only)\n"
- " Warning (Win32): this switch is provided automatically when the service\n"
- " is started from the control panel\n\n"
+ " Warning (Win32): this switch is provided automatically when\n"
+ " the service is started from the control panel\n\n"
+#ifndef _WIN32
+ " -i run in inetd mode (UNIX only)\n\n"
+#endif
" -s <file> save the current configuration to file\n\n"
" -f <file> load the current configuration from file; all switches\n"
" specified from the command line are ignored\n\n"
int main(int argc, char *argv[])
{
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 isdaemon = 0; // Non-zero if the user wants to run this program as a daemon
+#ifndef _WIN32
+ int isrunbyinetd = 0; // Non-zero if this is being run by inetd or something inetd-like
+#endif
int retval; // keeps the returning value from several functions
char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed
#ifndef _WIN32
mainhints.ai_socktype = SOCK_STREAM;
// Getting the proper command line options
- while ((retval = getopt(argc, argv, "b:dhp:4l:na:s:f:v")) != -1)
+ while ((retval = getopt(argc, argv, "b:dhip:4l:na:s:f:v")) != -1)
{
switch (retval)
{
case 'd':
isdaemon = 1;
break;
+ case 'i':
+#ifdef _WIN32
+ printusage();
+ exit(1);
+#else
+ isrunbyinetd = 1;
+#endif
+ break;
case 'n':
nullAuthAllowed = 1;
break;
case 'h':
printusage();
exit(0);
+ break;
default:
+ exit(1);
break;
}
}
+#ifndef _WIN32
+ if (isdaemon && isrunbyinetd)
+ {
+ fprintf(stderr, "rpcapd: -d and -i can't be used together\n");
+ exit(1);
+ }
+#endif
+
if (savefile[0] && fileconf_save(savefile))
- SOCK_MESSAGE("Error when saving the configuration to file");
+ SOCK_MESSAGE("Error when saving the configuration to file");
// If the file does not exist, it keeps the settings provided by the command line
if (loadfile[0])
signal(SIGPIPE, SIG_IGN);
#endif
- // forking a daemon, if it is needed
+#ifndef _WIN32
+ if (isrunbyinetd)
+ {
+ //
+ // -i was specified, indicating that this is being run
+ // by inetd or something that can run network daemons
+ // as if it were inetd (xinetd, launchd, systemd, etc.).
+ //
+ // Our standard input is the input side of a connection,
+ // and our standard output is the output side of a
+ // connection.
+ //
+ int sockctrl_in, sockctrl_out;
+ int devnull_fd;
+
+ //
+ // Duplicate the standard input and output, making them
+ // the input and output side of the control connection.
+ //
+ sockctrl_in = dup(0);
+ if (sockctrl_in == -1)
+ {
+ sock_geterror(NULL, errbuf, PCAP_ERRBUF_SIZE);
+ rpcapd_log(LOGPRIO_ERROR, "Can't dup standard input: %s",
+ errbuf);
+ exit(2);
+ }
+ sockctrl_out = dup(1);
+ if (sockctrl_out == -1)
+ {
+ sock_geterror(NULL, errbuf, PCAP_ERRBUF_SIZE);
+ rpcapd_log(LOGPRIO_ERROR, "Can't dup standard output: %s",
+ errbuf);
+ exit(2);
+ }
+
+ //
+ // Try to set the standard input and output to /dev/null.
+ //
+ devnull_fd = open("/dev/null", O_RDWR);
+ if (devnull_fd != -1)
+ {
+ //
+ // If this fails, just drive on.
+ //
+ (void)dup2(devnull_fd, 0);
+ (void)dup2(devnull_fd, 1);
+ close(devnull_fd);
+ }
+
+ //
+ // Handle this client.
+ // This is passive mode, so we don't care whether we were
+ // told by the client to close.
+ //
+ (void)daemon_serviceloop(sockctrl_in, sockctrl_out, 0,
+ nullAuthAllowed);
+
+ //
+ // Nothing more to do.
+ //
+ exit(0);
+ }
+#endif
+
if (isdaemon)
{
+ //
+ // This is being run as a daemon.
+ // On UN*X, it might be manually run, or run from an
+ // rc file.
+ //
#ifndef _WIN32
int pid;
+ //
+ // Daemonize ourselves.
+ //
// Unix Network Programming, pg 336
+ //
if ((pid = fork()) != 0)
exit(0); // Parent terminates
// umask(0);
// chdir("/");
#else
+ //
+ // This is being run as a service on Windows.
+ //
// If this call succeeds, it is blocking on Win32
+ //
if (svc_start() != 1)
SOCK_MESSAGE("Unable to start the service");
#ifdef _WIN32
HANDLE threadId; // handle for the subthread
u_long off = 0;
+ SOCKET *sockctrl_temp;
#else
pid_t pid;
#endif
- struct daemon_slpars *pars; // parameters needed by the daemon_serviceloop()
// Initialize errbuf
memset(errbuf, 0, sizeof(errbuf));
return;
}
- // 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)
+ //
+ // Allocate a location to hold the value of sockctrl.
+ // It will be freed in the newly-created thread once it's
+ // finished with it.
+ // I guess we *could* just cast sockctrl to a void *, but that's
+ // a bit ugly.
+ //
+ sockctrl_temp = (SOCKET *)malloc(sizeof (SOCKET));
+ if (sockctrl_temp == NULL)
{
pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
errno, "malloc() failed");
sock_close(sockctrl, NULL, 0);
return;
}
+ *sockctrl_temp = sockctrl;
- pars->sockctrl = sockctrl;
- pars->activeclose = 0; // useless in passive mode
- pars->isactive = 0;
- pars->nullAuthAllowed = nullAuthAllowed;
-
- threadId = (HANDLE)_beginthreadex(NULL, 0, daemon_serviceloop,
- (void *) pars, 0, NULL);
+ threadId = (HANDLE)_beginthreadex(NULL, 0,
+ main_passive_serviceloop_thread, (void *) sockctrl_temp, 0, NULL);
if (threadId == 0)
{
pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error creating the child thread");
rpcap_senderror(sockctrl, 0, PCAP_ERR_OPEN, errbuf, NULL);
sock_close(sockctrl, NULL, 0);
+ free(sockctrl_temp);
return;
}
CloseHandle(threadId);
{
//
// Child process.
- // This is passive mode, so this variable is deallocated
- // by the daemon_serviceloop().
- //
- pars = (struct daemon_slpars *) malloc (sizeof(struct daemon_slpars));
- if (pars == NULL)
- {
- pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Can't allocate memory for the child process");
- rpcap_senderror(sockctrl, 0, PCAP_ERR_OPEN, errbuf, NULL);
- sock_close(sockctrl, NULL, 0);
- return;
- }
-
- pars->sockctrl = sockctrl;
- pars->activeclose = 0; // useless in passive mode
- pars->isactive = 0;
- pars->nullAuthAllowed = nullAuthAllowed;
-
//
// Close the socket on which we're listening (must
// be open only in the parent).
//
closesocket(listen_sock);
+#if 0
+ //
+ // Modify thread params so that it can be killed at any time
+ // XXX - is this necessary? This is the main and, currently,
+ // only thread in the child process, and nobody tries to
+ // cancel us, although *we* may cancel the thread that's
+ // handling the capture loop.
+ //
+ if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL))
+ goto end;
+ if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL))
+ goto end;
+#endif
+
//
// Run the service loop.
+ // This is passive mode, so we don't care whether we were
+ // told by the client to close.
//
- daemon_serviceloop((void *) pars);
+ (void)daemon_serviceloop(sockctrl, sockctrl, 0,
+ nullAuthAllowed);
+
+ close(sockctrl);
+
exit(0);
}
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;
continue;
}
- pars = (struct daemon_slpars *) malloc (sizeof(struct daemon_slpars));
- if (pars == NULL)
- {
- pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
- errno, "malloc() failed");
- continue;
- }
-
- pars->sockctrl = sockctrl;
- pars->activeclose = 0;
- pars->isactive = 1;
- pars->nullAuthAllowed = nullAuthAllowed;
-
- daemon_serviceloop((void *) pars);
-
- activeclose = pars->activeclose;
+ activeclose = daemon_serviceloop(sockctrl, sockctrl, 1,
+ nullAuthAllowed);
- free(pars);
+ sock_close(sockctrl, NULL, 0);
// If the connection is closed by the user explicitely, don't try to connect to it again
// just exit the program
freeaddrinfo(addrinfo);
return 0;
}
+
+#ifdef _WIN32
+//
+// Main routine of a passive-mode service thread.
+//
+unsigned __stdcall main_passive_serviceloop_thread(void *ptr)
+{
+ SOCKET sockctrl;
+
+ sockctrl = *((SOCKET *)ptr);
+ free(ptr);
+
+ //
+ // Handle this client.
+ // This is passive mode, so we don't care whether we were
+ // told by the client to close.
+ //
+ (void)daemon_serviceloop(sockctrl, sockctrl, 0, nullAuthAllowed);
+
+ sock_close(sockctrl, NULL, 0);
+
+ return 0;
+}
+#endif