/*
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
/*
* BSD 3 Clause License
*
* Copyright (c) 2007, The Storage Networking Industry Association.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* distribution.
*
* - Neither the name of The Storage Networking Industry Association (SNIA)
* nor the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/* Copyright (c) 2007, The Storage Networking Industry Association. */
/* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <netdb.h>
#include <libinetutil.h>
#include "ndmpd.h"
#include "ndmpd_common.h"
#define NDMP_PROC_REP 0
/*
* The ndmp connection version can be set through command line. If command line
* is not specified it will be set from the ndmp SMF version property.
*/
int ndmp_ver = 0;
/*
* The NDMP listening port number
*/
int ndmp_port = 0;
/*
* Restore path mechanism definition
* 0 means partial path restore and
* 1 means full path restore.
* Refer to NDMP_FULL_RESTORE_PATH for partial path and full path definition.
*/
/*
* Do we support Direct Access Restore?
*/
int ndmp_dar_support = 0;
/*
* ndmp_connection_t handler function
*/
extern ndmp_handler_t ndmp_msghdl_tab[];
static int ndmp_readit(void *connection_handle,
int len);
static int ndmp_writeit(void *connection_handle,
int len);
void *ndmpd_worker(void *ptarg);
#ifdef lint
{
return (0);
}
#endif /* lint */
/*
* ndmp_create_connection
*
* Allocate and initialize a connection structure.
*
* Parameters:
* handler_tbl (input) - message handlers.
*
* Returns:
* NULL - error
* connection pointer
*
* Notes:
* The returned connection should be destroyed using
* ndmp_destroy_connection().
*/
ndmp_create_connection(void)
{
if (connection == NULL)
return (NULL);
connection->conn_my_sequence = 0;
connection->conn_client_data = 0;
return (0);
}
return ((ndmp_connection_t *)connection);
}
/*
* ndmp_destroy_connection
*
* Shutdown a connection and release allocated resources.
*
* Parameters:
* connection_handle (Input) - connection handle.
*
* Returns:
* void
*/
void
{
if (connection->conn_sock >= 0) {
}
}
/*
* ndmp_close
*
* Close a connection.
*
* Parameters:
* connection_handle (Input) - connection handle.
*
* Returns:
* void
*/
void
{
if (connection->conn_sock >= 0) {
}
/*
* We should close all the tapes that are used by this connection.
* In some cases the ndmp client opens a tape, but does not close the
* tape and closes the connection.
*/
}
/*
* ndmp_start_worker
*
* Initializes and starts a ndmp_worker thread
*/
int
{
int rc;
(void) pthread_attr_init(&tattr);
(void) pthread_attr_destroy(&tattr);
return (rc);
}
/*
* ndmp_run
*
* Creates a socket for listening and accepting connections
* from NDMP clients.
* Accepts connections and passes each connection to the connection
* handler.
*
* Parameters:
* port (input) - NDMP server port.
* If 0, the port number will be retrieved from
* the network service database. If not found there,
* the default NDMP port number (from ndmp.x)
* will be used.
* handler (input) - connection handler function.
*
* Returns:
* This function normally never returns unless there's error.
* -1 : error
*
* Notes:
* This function does not return unless encountering an error
* related to the listen socket.
*/
int
{
int ns;
int on;
int server_socket;
unsigned int ipaddr;
return (-1);
}
on = 1;
(void) close(server_socket);
return (-1);
}
(void) close(server_socket);
return (-1);
}
for (; ; ) {
continue;
}
(void) ndmp_start_worker(argp);
}
}
}
/*
* ndmpd_worker thread
*
* Parameters:
* argp (input) - structure containing socket and handler function
*
* Returns:
* 0 - successful connection.
* -1 - error.
*/
void *
{
int sock;
if (!argp)
return ((void *)-1);
exit(1);
}
/* initialize auditing session */
return ((void *)-1);
}
return (NULL);
}
/*
* ndmp_process_requests
*
* Reads the next request message into the stream buffer.
* Processes messages until the stream buffer is empty.
*
* Parameters:
* connection_handle (input) - connection handle.
*
* Returns:
* 0 - 1 or more messages successfully processed.
* -1 - error; connection no longer established.
*/
int
{
int rv;
rv = 0;
rv = -1;
return (rv);
}
/*
* ndmp_send_request
*
* Send an NDMP request message.
*
* Parameters:
* connection_handle (input) - connection pointer.
* message (input) - message number.
* err (input) - error code to place in header.
* request_data (input) - message body.
* reply (output) - reply message. If 0, reply will be
* discarded.
*
* Returns:
* 0 - successful send.
* -1 - error.
* otherwise - error from reply header.
*
* Notes:
* - The reply body is only returned if the error code is NDMP_NO_ERR.
*/
int
{
int r;
/* Lookup info necessary for processing this request. */
message);
return (-1);
}
(void) gettimeofday(&time, 0);
header.reply_sequence = 0;
"Sending message 0x%x: encoding request header", message);
return (-1);
}
request_data)) {
"Sending message 0x%x: encoding request body",
message);
return (-1);
}
}
if (handler->mh_xdr_reply == 0) {
return (0);
}
/*
* Process messages until the reply to this request has been
* processed.
*/
for (; ; ) {
/* connection error? */
if (r < 0)
return (-1);
/* no reply received? */
if (r == 0)
continue;
/* reply received? */
if (r == 1) {
if (message !=
"Received unexpected reply 0x%x",
return (-1);
}
else
}
/* error handling reply */
return (-1);
}
}
/*
* ndmp_send_request_lock
*
* A wrapper for ndmp_send_request with locks.
*
* Parameters:
* connection_handle (input) - connection pointer.
* message (input) - message number.
* err (input) - error code to place in header.
* request_data (input) - message body.
* reply (output) - reply message. If 0, reply will be
* discarded.
*
* Returns:
* 0 - successful send.
* -1 - error.
* otherwise - error from reply header.
*
* Notes:
* - The reply body is only returned if the error code is NDMP_NO_ERR.
*/
int
{
int rv;
reply);
return (rv);
}
/*
* ndmp_send_response
*
* Send an NDMP reply message.
*
* Parameters:
* connection_handle (input) - connection pointer.
* err (input) - error code to place in header.
* reply (input) - reply message body.
*
* Returns:
* 0 - successful send.
* -1 - error.
*
* Notes:
* - The body is only sent if the error code is NDMP_NO_ERR.
*/
int
void *reply)
{
(void) gettimeofday(&time, 0);
"encoding reply header",
return (-1);
}
if (err == NDMP_NO_ERR &&
reply) {
"Sending message 0x%x: encoding reply body",
return (-1);
}
}
return (0);
}
/*
* ndmp_free_message
*
* Free the memory of NDMP message body.
*
* Parameters:
* connection_handle (input) - connection pointer.
*
* Returns:
* void
*
*/
void
{
return;
} else {
}
}
/*
* ndmp_get_fd
*
* Returns the connection file descriptor.
*
* Parameters:
* connection_handle (input) - connection handle
*
* Returns:
* >=0 - file descriptor.
* -1 - connection not open.
*/
int
{
}
/*
* ndmp_set_client_data
*
* This function provides a means for the library client to provide
* a pointer to some user data structure that is retrievable by
* each message handler via ndmp_get_client_data.
*
* Parameters:
* connection_handle (input) - connection handle.
* client_data (input) - user data pointer.
*
* Returns:
* void
*/
void
{
}
/*
* ndmp_get_client_data
*
* This function provides a means for the library client to provide
* a pointer to some user data structure that is retrievable by
* each message handler via ndmp_get_client_data.
*
* Parameters:
* connection_handle (input) - connection handle.
*
* Returns:
* client data pointer.
*/
void *
{
}
/*
* ndmp_set_version
*
* Sets the NDMP protocol version to be used on the connection.
*
* Parameters:
* connection_handle (input) - connection handle.
* version (input) - protocol version.
*
* Returns:
* void
*/
void
{
}
/*
* ndmp_get_version
*
* Gets the NDMP protocol version in use on the connection.
*
* Parameters:
* connection_handle (input) - connection handle.
* version (input) - protocol version.
*
* Returns:
* void
*/
{
}
/*
* ndmp_set_authorized
*
* Mark the connection as either having been authorized or not.
*
* Parameters:
* connection_handle (input) - connection handle.
* authorized (input) - TRUE or FALSE.
*
* Returns:
* void
*/
void
{
}
/*
* ndmpd_main
*
* NDMP main function called from main().
*
* Parameters:
* void
*
* Returns:
* void
*/
void
ndmpd_main(void)
{
char *propval;
/*
* Find ndmp port number to be used. If ndmpd is run as command line
* and port number is supplied, use that port number. If port number is
* is not supplied, find out if ndmp port property is set. If ndmp
* port property is set, use that port number otherwise use the defaule
* port number.
*/
if (ndmp_port == 0) {
*propval == 0)
else
}
perror("ndmp_run ERROR");
}
/*
* connection_handler
*
* NDMP connection handler.
* Waits for, reads, and processes NDMP requests on a connection.
*
* Parameters:
* connection (input) - connection handle.
*
* Return:
* void
*/
void
{
int connection_fd;
/*
* The 'protocol_version' must be 1 at first, since the client talks
* to the server in version 1 then they can move to a higher
* protocol version.
*/
(void) ndmpd_data_init(&session);
if (ndmpd_mover_init(&session) < 0)
return;
if (ndmp_lbr_init(&session) < 0)
return;
/*
* Setup defaults here. The init functions can not set defaults
* since the init functions are called by the stop request handlers
* and client set variables need to persist across data operations.
*/
NDMP_NO_ERR, (void *)&req, 0) < 0) {
return;
}
/*
* Add the handler function for the connection to the DMA.
*/
return;
}
/*
* Register the connection in the list of active connections.
*/
"Could not register the session to the server.");
return;
}
}
}
(void) ndmp_connect_list_del(connection);
}
/*
* connection_file_handler
*
* ndmp_connection_t file handler function.
* Called by ndmpd_select when data is available to be read on the
* NDMP connection.
*
* Parameters:
* cookie (input) - session pointer.
* fd (input) - connection file descriptor.
* mode (input) - select mode.
*
* Returns:
* void.
*/
/*ARGSUSED*/
static void
{
}
/* ************* private functions *************************************** */
/*
* ndmp_readit
*
* Low level read routine called by the xdrrec library.
*
* Parameters:
* connection (input) - connection pointer.
* buf (input) - location to store received data.
* len (input) - max number of bytes to read.
*
* Returns:
* >0 - number of bytes received.
* -1 - error.
*/
static int
{
if (len <= 0) {
/* ndmp_connection_t has been closed. */
return (-1);
}
return (len);
}
/*
* ndmp_writeit
*
* Low level write routine called by the xdrrec library.
*
* Parameters:
* connection (input) - connection pointer.
* buf (input) - location to store received data.
* len (input) - max number of bytes to read.
*
* Returns:
* >0 - number of bytes sent.
* -1 - error.
*/
static int
{
register int n;
register int cnt;
return (-1);
}
}
return (len);
}
/*
* ndmp_recv_msg
*
* Read the next message.
*
* Parameters:
* connection (input) - connection pointer.
* msg (output) - received message.
*
* Returns:
* 0 - Message successfully received.
* error number - Message related error.
* -1 - Error decoding the message header.
*/
static int
{
/* Decode the header. */
return (-1);
/* Lookup info necessary for processing this message. */
return (NDMP_NOT_SUPPORTED_ERR);
}
return (0);
/* Determine body type */
"Processing request 0x%x:connection not authorized",
return (NDMP_NOT_AUTHORIZED_ERR);
}
0) {
xdr_func =
"Processing request 0x%x: no xdr function "
"in handler table",
return (NDMP_NOT_SUPPORTED_ERR);
}
return (NDMP_NO_MEM_ERR);
}
} else {
xdr_func =
"Processing reply 0x%x: no xdr function "
"in handler table",
return (NDMP_NOT_SUPPORTED_ERR);
}
return (NDMP_NO_MEM_ERR);
}
}
/* Decode message arguments if needed */
if (xdr_func) {
"Processing message 0x%x: error decoding arguments",
return (NDMP_XDR_DECODE_ERR);
}
}
return (0);
}
/*
* ndmp_process_messages
*
* Reads the next message into the stream buffer.
* Processes messages until the stream buffer is empty.
*
* This function processes all data in the stream buffer before returning.
* This allows functions like poll() to be used to determine when new
* messages have arrived. If only some of the messages in the stream buffer
* were processed and then poll was called, poll() could block waiting for
* a message that had already been received and read into the stream buffer.
*
* This function processes both request and reply messages.
* Request messages are dispatched using the appropriate function from the
* message handling table.
* Only one reply messages may be pending receipt at a time.
* A reply message, if received, is placed in connection->conn_msginfo
* before returning to the caller.
* Errors are reported if a reply is received but not expected or if
* more than one reply message is received
*
* Parameters:
* connection (input) - connection pointer.
* reply_expected (output) - TRUE - a reply message is expected.
* FALSE - no reply message is expected and
* an error will be reported if a reply
* is received.
*
* Returns:
* NDMP_PROC_REP_ERR - 1 or more messages successfully processed,
* error processing reply message.
* NDMP_PROC_REP_ERR - 1 or more messages successfully processed,
* reply seen.
* NDMP_PROC_REP_ERR - 1 or more messages successfully processed,
* no reply seen.
* NDMP_PROC_REP_ERR - error; connection no longer established.
*
* Notes:
* If the peer is generating a large number of requests, a caller
* looking for a reply will be blocked while the requests are handled.
* This is because this function does not return until the stream
* buffer is empty.
* Code needs to be added to allow a return if the stream buffer
* is not empty but there is data available on the socket. This will
* prevent poll() from blocking and prevent a caller looking for a reply
* from getting blocked by a bunch of requests.
*/
static int
{
int err;
do {
sizeof (msg_info_t));
if (connection->conn_eof) {
return (NDMP_PROC_ERR);
}
if (err < 1) {
/*
* Error occurred decoding the header.
* Don't send a reply since we don't know
* the message or if the message was even
* a request message. To be safe, assume
* that the message was a reply if a reply
* was expected. Need to do this to prevent
* hanging ndmp_send_request() waiting for a
* reply. Don't set reply_read so that the
* reply will be processed if it is received
* later.
*/
if (reply_read == FALSE)
reply_error = TRUE;
continue;
}
!= NDMP_MESSAGE_REQUEST) {
if (reply_expected == FALSE ||
reply_read == TRUE)
"Unexpected reply message: 0x%x",
message);
if (reply_read == FALSE) {
reply_read = TRUE;
reply_error = TRUE;
}
continue;
}
(void) ndmp_send_response((ndmp_connection_t *)
continue;
}
!= NDMP_MESSAGE_REQUEST) {
"Unexpected reply message: 0x%x",
continue;
}
reply_read = TRUE;
continue;
}
/*
* The following is needed to catch an improperly constructed
* handler table or to deal with an NDMP client that is not
* conforming to the negotiated protocol version.
*/
(void) ndmp_send_response((ndmp_connection_t *)
continue;
}
/*
* Call the handler function.
* The handler will send any necessary reply.
*/
if (reply_msginfo.mi_body)
return (NDMP_PROC_ERR);
}
if (reply_error) {
if (reply_msginfo.mi_body)
return (NDMP_PROC_REP_ERR);
}
if (reply_read) {
return (NDMP_PROC_MSG);
}
return (NDMP_PROC_REP);
}
/*
* ndmp_get_interface
*
* Return the NDMP interface (e.g. config, scsi, tape) for the
* specific message.
*
* Parameters:
* message (input) - message number.
*
* Returns:
* NULL - message not found.
* pointer to handler info.
*/
static ndmp_handler_t *
{
return (NULL);
/* Sanity check */
return (NULL);
return (ni);
}
/*
* ndmp_get_handler
*
* Return the handler info for the specified NDMP message.
*
* Parameters:
* connection (input) - connection pointer.
* message (input) - message number.
*
* Returns:
* NULL - message not found.
* pointer to handler info.
*/
static ndmp_msg_handler_t *
{
if (ni)
return (handler);
}
/*
* ndmp_check_auth_required
*
* Check if the connection needs to be authenticated before
* this message is being processed.
*
* Parameters:
* message (input) - message number.
*
* Returns:
* TRUE - required
* FALSE - not required
*/
static boolean_t
{
if (ni)
return (auth_req);
}
/*
* tcp_accept
*
* A wrapper around accept for retrying and getting the IP address
*
* Parameters:
* listen_sock (input) - the socket for listening
* inaddr_p (output) - the IP address of peer connection
*
* Returns:
* socket for the accepted connection
* -1: error
*/
int
{
int sock, i;
int try;
i = sizeof (sin);
if (sock < 0) {
continue;
}
return (sock);
}
return (-1);
}
/*
* tcp_get_peer
*
* Get the peer IP address for a connection
*
* Parameters:
* sock (input) - the active socket
* inaddr_p (output) - the IP address of peer connection
* port_p (output) - the port number of peer connection
*
* Returns:
* socket for the accepted connection
* -1: error
*/
int
{
int i, rc;
i = sizeof (sin);
if (rc != 0)
return (-1);
if (inaddr_p)
if (port_p)
return (sock);
}
/*
* gethostaddr
*
* Get the IP address string of the current host
*
* Parameters:
* void
*
* Returns:
* IP address
* NULL: error
*/
char *
gethostaddr(void)
{
static char s[MAXHOSTNAMELEN];
struct hostent *h;
char *p;
if (gethostname(s, sizeof (s)) == -1)
return (NULL);
if ((h = gethostbyname(s)) == NULL)
return (NULL);
p = h->h_addr_list[0];
}
/*
* get_default_nic_addr
*
* Get the IP address of the default NIC
*/
char *
get_default_nic_addr(void)
{
int nifs;
if (nifs <= 0)
return (NULL);
/* pick the first interface's address */
}
/*
* ndmpd_audit_backup
*
* Generate AUE_ndmp_backup audit record
*/
/*ARGSUSED*/
void
{
return;
}
if (dest == NDMP_ADDR_LOCAL) {
} else {
}
if (result == 0) {
} else {
}
}
/*
* ndmpd_audit_restore
*
* Generate AUE_ndmp_restore audit record
*/
/*ARGSUSED*/
void
{
ADT_ndmp_restore)) == NULL) {
return;
}
if (dest == NDMP_ADDR_LOCAL) {
} else {
}
if (result == 0) {
} else {
}
}
/*
* ndmpd_audit_connect
*
* Generate AUE_ndmp_connect audit record
*/
/*ARGSUSED*/
void
{
return;
}
return;
}
ADT_ndmp_connect)) == NULL) {
return;
}
if (result == 0) {
} else {
}
}
/*
* ndmpd_audit_disconnect
*
* Generate AUE_ndmp_disconnect audit record
*/
/*ARGSUSED*/
void
{
ADT_ndmp_disconnect)) == NULL) {
return;
}
}
void *
{
void *data;
}
return (data);
}
/*
* get_backup_path_v3
*
* Get the backup path from the NDMP environment variables.
*
* Parameters:
* params (input) - pointer to the parameters structure.
*
* Returns:
* The backup path: if anything is specified
* NULL: Otherwise
*/
char *
{
char *bkpath;
if (!bkpath)
if (!bkpath) {
"Backup path not defined.\n");
} else {
}
return (bkpath);
}
/*
* get_backup_path
*
* Find the backup path from the environment variables (v2)
*/
char *
{
char *bkpath;
return (NULL);
}
if (*bkpath != '/') {
return (NULL);
}
return (bkpath);
}