/* ************************************************************************ ** ** Copyright (c) 2010..2013 by ** Core|Vision B.V. ** Cereslaan 10b ** 5384 VT Heesch ** The Netherlands ** ** All Rights Reserved ** ************************************************************************ */ /* ************************************************************************ ** ** Project name: Dual Inventive: Utility Library ** Filename: cp3000.h ** Author: Jack Weeland ** Date: January 27, 2010 ** File version: $Revision: 1.23 $ ** $Date: 2014/01/23 15:57:28 $ ** ************************************************************************ */ /* ************************************************************************ ** ** CP3000 high(er) level protocol handling ** ** High level functions: ** - Create a server socket, accept clients ** - Create a client socket ** Utility functions: ** - The I/O buffers ** - Checking the validity of a message (checksum) ** - Escape and unescape strings, etc ** ************************************************************************ */ #ifndef __CP3000_H #define __CP3000_H #include #include #include #include // for 'fd_set' /* ** Definitions - tokenizer */ // Flags for 'token_definition_t.flags' // - Do not send the status in a reply to the originator; must be set also // when the handler sends a status reply itself (but _not_ when it only // sends intermediate replies, leaving the status reply to the library) // or when the reply is sent asynchronously. // - The handler _cannot_ send additional information in the reply, only // a status code. If handler needs to supply additional information, it // _must_ define the CP3000_SELF_REPLY flag. #define CP3000_DEFAULT_REPLY 0 #define CP3000_NO_REPLY 0x00000001 #define CP3000_SELF_REPLY (CP3000_NO_REPLY) #define CP3000_ASYNC_REPLY (CP3000_NO_REPLY) // - Match only the first part of a token; as the 'token_definition_t' // array will be handled in order, a partial match must always follow // the entries that must be matched exactly #define CP3000_MATCH_PARTIAL 0x00000100 // - Handle tokens (data) externally; must be the last item in the list // and the "cmd" _must_ be NULL #define CP3000_MATCH_ANY 0x00000200 // Security clearance level; a CP3000 command is only executed when the // security level of a command is less than or equal to the security // level of a device, as set with 'cp3000_set_authorization()' #define CP3000_CLEARANCE(level) ((level) << 16) #define CP3000_CLEARANCE_MASK 0xFFFF0000 // - Special flag for the reply code, to be used in the token_definition_t // array as: // { CP3000_REPLY, reply_handler, CP3000_REPLY_TOKEN } // It also inhibits cp3000_process_data() from sending a reply, as this // would result in a bouncing of replies on replies #define CP3000_REPLY_TOKEN (CP3000_MATCH_PARTIAL | CP3000_NO_REPLY) #define CP3000_CMD_TOKEN (CP3000_MATCH_PARTIAL | CP3000_NO_REPLY) // Special characters in CP3000 commands #define CP3000_REPLY (TCPFMT_TOKEN_REPLY) #define CP3000_CMD (TCPFMT_TOKEN_CMD) #define CP3000_SEP (TCPFMT_TOKEN_SEP) #define CP3000_CLIENT (TCPFMT_TOKEN_CLIENTSEP) #define CP3000_CHKSUM (TCPFMT_TOKEN_CHECKSUM) // Placeholder for 'token_info_t.datasz', most notably for the 'datasz' // argument for 'cp3000_create_token()' #define CP3000_CMDTOKEN (-1) /* ** Definitions - device flags */ // Flags for the default CP3000 parser, set with 'cp3000_set_flags()' // - Don't check the checksum in the message #define CP3000_IGNORE_CHECKSUM 0x00010000 // - Return raw data in 'token->cmd'; the argument list will be empty // No checksum will be calculated nor checked #define CP3000_RAW_TOKENS 0x00020000 // - Return binary data in 'token->data'; the size of the data buffer // is stored in 'token->data_len'; the argument list will be empty // No checksum will be calculated nor checked #define CP3000_BINARY 0x00040000 // Temporary flag while receiving replies: do not process the putback // list (should be modified with 'cp3000_set_flag()' and 'cp3000_clear_flag()' #define CP3000_DONT_PROCESS_PUTBACK 0x80000000 /* ** Definitions - data types */ // Structure to hold the internal state typedef struct CP3000_DEVICE *cp3000_device_t; // Public and private keys and security certificates typedef struct CP3000_KEY *cp3000_key_t; // CP3000 tokens in the message from the client typedef struct TOKEN_INFO { union { const char *cmd; // command (or reply), the first token void *data; // generic, binary data; 'args' is not used }; int datasz; // number of bytes in the buffer pointed to by 'data'; // not used for strings lst_t args; // argument list int client; // client identifier, or '-1' when missing or not used int status; // '-1' for a command or the status code from the client } *token_info_t; // Handler definition, which is called with the arguments of the // command and an application defined context typedef int (*token_handler_t)(cp3000_device_t, token_info_t, void*); // token definition typedef struct TOKEN_DEFINITION { const char *cmd; // command or place holder for a reply (CP3000_REPLY below) token_handler_t handler; // handler void *param; // pointer to paramters for the handler unsigned int flags; // flags as defined above } token_definition_t; /* ** Definitions - extended mode parser */ // Place-holder for default CP3000 command processing #define CP3000_DEFAULT_PARSER NULL // Callback functions for the command parser // // Functions to initialize and destroy an optional private data structure for the // parser // Parameters: // - parent cp3000_device_t object // - parser parameters; see below // Remark: // For the 'init' function, the pointer 'param' initially points to 'param' argument // for 'cp3000_add_parser()'. The 'init' function can allocate its own data structure // (thus overriding the original 'param', which should be NULL in that case to avoid // confusion). // The 'destroy' function must deallocate this data structure in this scenario. // Note: // These functions can be NULL when they are not needed. typedef int (*cp3000_parser_initfcn_t)(cp3000_device_t, void**); typedef int (*cp3000_parser_destroyfcn_t)(cp3000_device_t, void*); // Match the data in the buffer to check if it is the format expected by the parser // Parameters: // - parent cp3000_device_t object // - parser parameters // - pointer to the data buffer (data to match) // - number of bytes in the data buffer // Returns: // < 0 on error or data not for this parser // 0 if there isn't enough data, but buffer could contain a valid command // > 0 successful match; return value is the number of bytes that can be // processed // Note: // The value returned by the string match function determines how many bytes // will be consumed from the I/O buffers. The data in the I/O buffers therefore // _must_ be processed by this parser. typedef int (*cp3000_parser_strmatchfcn_t)(cp3000_device_t, void *param, const void *buffer, size_t n); // Process data // Parameters: // - parent cp3000_device_t object // - parser parameters // - pointer to the data buffer (data to process) // - number of bytes in the data buffer // Returns: // < 0 on error or data not for this parser // >= 0 command processed succesfully // Note: // At this point, the data has already been removed from the I/O buffers, so // it is too late to determine during processor that the data is not intended // for this parser. This _must_ be correctly determined in the string match // member function. typedef int (*cp3000_parser_processfcn_t)(cp3000_device_t, void *param, const void *buffer, size_t n); // The command parser definition simply contains the the callback functions. typedef const struct TOKEN_PARSER { // parser name, for debugging const char *name; // callbacks cp3000_parser_initfcn_t cp3000_parser_init; cp3000_parser_destroyfcn_t cp3000_parser_destroy; cp3000_parser_strmatchfcn_t cp3000_parser_strmatch; cp3000_parser_processfcn_t cp3000_parser_process; } *token_parser_t; /* ** Initialization */ // Initialization, must be called before anything else at the start of // the application. // The multi-threaded version also initializes multi-threading specific // stuff. The 'mt' function calls the normal 'cp3000_init()'. // Returns -1 on failure and 0 on success int cp3000_init(); int cp3000_init_mt(); // De-initialization int cp3000_deinit(); int cp3000_deinit_mt(); /* ** Client and server */ // Create a device. The device must be initialized to be a server or // client using one of the 'cp3000_init_xxx()' functions that follow // in the next sections. // Returns NULL on error cp3000_device_t cp3000_create_device(); // Destruction // - Closes socket(s) and delete all OpenSSL related objects // - Destroys the cp3000_device_t object // Parameters: // cp3000_device_t object to close and destroy // Returns: // -1 on error int cp3000_destroy_device(cp3000_device_t); // Attach security certificates to a device to enable transport layer // security using OpenSSL. This must be done _before_ the device is // initialized as a server or client. // Parameters: // cp3000_device_t object // See also the functions defined in 'cp3000-cert.h' // Important note: the OpenSSL functions have the peculiarity that they // destroy their keys after use; the 'cp3000_key_t' parameters are // therefore copied. int cp3000_make_secure( cp3000_device_t, cp3000_key_t public_cert, // signed public certificate cp3000_key_t trust_cert, // certificate of the CA Server cp3000_key_t private_key // private key ); // Test if the connection is secure int cp3000_is_secure(cp3000_device_t); // Initialize the SSL context, connect the read and write BIO to TLS/SSL // and do the SSL handshake. Also sets the peer's address for non-secure // clients. // Parameters: // The cp3000_device_t object // Returns: // 0 on succes, -1 on error int cp3000_init_connection(cp3000_device_t); // Close connection or file associated with this CP3000 object. // User objects, flags and security descriptors will be retained and can be reused. // Parameters: // The cp3000_device_t object // Returns: // 0 on succes, -1 on error int cp3000_disconnect(cp3000_device_t); /* ** Server side */ // Initialize server // - Creates a server socket. // Parameters: // cp3000_device_t object // port - port number to listen to (-1 to use default from config or /etc/services) int cp3000_init_server( cp3000_device_t, int port ); // Wait for a client and accept the client. // After accepting the client, the function 'cp3000_init_connection()' // must be called, usually after the 'fork(2)'. // Parameters: // cp3000_device_t object for the server // Returns: // cp3000_device_t for the client // NULL on error // Remarks: // Initializes the SSL object from 'server'. cp3000_device_t cp3000_accept_client( cp3000_device_t server ); /* ** Client side */ // Connect to a server // The function 'cp3000_init_connection()' must be called after this function. // Parameters: // cp3000_device_t object // server - hostname:port // Returns: // 0 on succes, -1 on error int cp3000_connect_to_server( cp3000_device_t, const char *server ); /* ** Files and sockets (file descriptor interface) */ // Attach a file descriptor to it. // The function 'cp3000_init_connection()' must be called after this function. // Used for all clients, also those of a Unix-socket so they can use the // CP3000 tokenizer and iobuffers // Returns 0 on success and -1 on error; int cp3000_attach_socket( cp3000_device_t, int fd ); // Attach a read and write file descriptor to it. // Used when a server is started from 'inetd' or 'xinetd' // The function 'cp3000_init_connection()' must be called after this function. // Returns 0 on success and -1 on error; // Remark: the file descriptors are _not_ closed when the cp3000_device_t object // is destroyed. int cp3000_attach_fd( cp3000_device_t, int rfd, int wfd ); // Open a file using 'open(2)' and attach if to the CP3000 object int cp3000_open_file( cp3000_device_t, const char *path, int open_flags ); /* ** Additional information */ // Get port number; returns -1 on error int cp3000_get_port(cp3000_device_t); // Get name of the peer const char *cp3000_get_peername(cp3000_device_t); // Byte counts unsigned long cp3000_get_recv_bytes(cp3000_device_t); unsigned long cp3000_get_send_bytes(cp3000_device_t); // Special flags; see the definitions above // The 'cp3000_set_flags()' function _replaces_ the flags int cp3000_set_flags(cp3000_device_t, unsigned int flags); unsigned int cp3000_get_flags(cp3000_device_t); // Set or clear individual flag(s) int cp3000_set_flag(cp3000_device_t, unsigned int flags); int cp3000_clear_flag(cp3000_device_t, unsigned int flags); /* ** Exported functions - user data */ // These two functions allow for adding user data to a cp3000_device_t // object. The 'set' function makes a copy of the user data, so it is // allowed to add an automatic object. // Parameters: // cp3000_device_t object // data - Optional initial data; may be NULL // datasz - Size of the user data, or '0' to delete the current data // Returns: // Pointer to the allocated buffer. // Remark: // This function always allocates a buffer when 'datasz' is non-zero // and it returns a pointer to this buffer. When 'data' is NULL, this // buffer will be uninitialized, otherwise 'data' is copied into it. void *cp3000_set_user_data(cp3000_device_t device, const void *data, size_t datasz); void *cp3000_get_user_data(cp3000_device_t); int cp3000_destroy_user_data(cp3000_device_t); /* ** Exported functions - extended (token) parser */ // Clear all parsers and add new ones // Parameters: // cp3000_device_t object // token_parser_t object (pointer to a 'struct TOKEN_PARSER') // parameter(s) for the parser functions // Returns: // Negative value on error // Remark: // The members of the 'token_parser_t' object are copied to an internal structure, // so the object passed to this function may be a local object (i.e. it doesn't // have to be a global or static object). int cp3000_clear_parsers(cp3000_device_t); int cp3000_add_parser(cp3000_device_t, token_parser_t, void *param); // Replace all existing parsers, if any, by this (single) parser // Simply calls 'cp3000_clear_parsers()' followed by 'cp3000_add_parser()'. int cp3000_set_parser(cp3000_device_t, token_parser_t, void *param); // Put data back into the buffer for later processing. // Parameters: // cp3000_device_t object // data // number of bytes // Returns: // < 0 on error, number of bytes otherwise // Notes: // The putback data is in fact consumed and stored an a separate buffer. When // calling 'cp3000_process_data()', the putback buffer will be consumed first. // The putback list is used to store data while waiting for a specific reply. int cp3000_parser_putback(cp3000_device_t, const void *data, size_t datasz); /* ** Exported functions - token parser main routine and default CP3000 command processor */ // Set a devices authorization level of executed comands int cp3000_set_authorization(cp3000_device_t, int level); // Check authorization // Returns boolean int cp3000_check_authorization(cp3000_device_t, int level); // Receive data from a CP3000 communication channel // Parameters: // cp3000_device_t object (the communication channel) // tokens - array of tokens that are recognised _or_ NULL to use // the (token) parsers attached to the device with 'cp3000_add_parser()' // Returns: // Negative value ('-1') on any error; there is no way to distinguish // which command returned the error, but 'di_errno' will be set to // the last error. // Negative value when the peer closed the communication channel; // 'di_errno' will be set to TCPERR_EOF in this case. // Number of commands/replies handled otherwise. // Remarks: // This function can be used to handle CP3000 commands only (simple // behaviour) or a mixture of CP3000 and foreign data formats (the // new, extended behaviour). // The following, using the extended usage, is equivalent to the old, // simple usage: // // extended // cp3000_clear_parsers(device); // cp3000_add_parser(device, CP3000_DEFAULT_PARSER, token_definitions); // ... // cp3000_process_data(device, NULL); // instead of // // simple // cp3000_process_data(device, token_definitions); // Remarks (CP3000 i.e. old style behaviour): // - Processes the putback list, filled by 'cp3000_recv_reply()', first // - Checks the I/O buffer before reading from the socket // - For each command read from the socket it will // - Check the input (checksum, authentification) // - Tokenize the input // - Call the associated handler // - Reply to the peer (if needed for the command, unless CP3000_NO_REPLY // is set in the flags for the token definition) int cp3000_process_data(cp3000_device_t, const token_definition_t *token_ctx); /* ** Exported functions - old-style tokenizer for manual parsing ** NB: these functions are called from 'cp3000_process_data()' */ // Check if data is available in the internal buffers; if there is, // the caller _mustn't_ call 'select(2)' to wait for data on the // socket, but consume the pending data fist // Returns '0' if there is none or a positive number otherwise int cp3000_data_pending(cp3000_device_t); // Process a tokenized CP3000 command int cp3000_process_token(cp3000_device_t, const token_definition_t*, token_info_t); // Tokenize a string buffer; returns the tokens found or NULL // in the case of an error // Parameters: // buffer - string to tokenize // Returns: // Token object; must be deleted by cp3000_destroy_tokens(). // Remark: // Called from cp3000_process_data(). // To be used for CP3000 commands only. token_info_t cp3000_tokenize(const char *buffer); // Duplicate a token // Parameters: // tokens // Returns: // Token object; must be deleted by cp3000_destroy_tokens(). // Note: // Token found in a 'cp3000_process_data()' cycle only have a limited // lifetime. To store them for later use, they must be duplicated with // this function. token_info_t cp3000_duplicate_token(const token_info_t); // Create an empty token or make a binary token // Parameters: // data // number of bytes, or zero when 'data' is a string // Returns: // Token object; must be deleted by cp3000_destroy_tokens(). // Notes: // Creates an empty token when 'datasz' is zero and 'data' is NULL. // Duplicates the string pointed to by 'data' when 'datasz' is zero. // Duplicates a binary data buffer of size 'datasz' otherwise. token_info_t cp3000_create_token(const void *data, size_t datasz); // Set arguments, status and client identifier in a token created with 'cp3000_create_token()' int cp3000_token_add_arg(token_info_t tokens, const char *arg); int cp3000_token_set_status(token_info_t tokens, int status); int cp3000_token_set_client(token_info_t tokens, int client_id); // Delete the token found by 'cp3000_tokenize()' or duplicated with 'cp3000_duplicate_token()' int cp3000_destroy_token(token_info_t tokens); // old name #define cp3000_delete_token(t) cp3000_destroy_token(t) /* ** Exported functions - CP3000 commands and replies */ // Send a command over the TCP channel. // Parameters: // cp3000_device_t object (the communication channel) // fmt - a TCPFMT_xxx with the command and its parameters // ... - arguments // Returns: // Negative value on error. // Remarks: // Caller must escape all parameters. int cp3000_send_cmd(cp3000_device_t, const char *fmt, ...); int cp3000_vsend_cmd(cp3000_device_t, const char *fmt, va_list ap); // Send a reply; 'reply' is optional and may be NULL // Parameters: // cp3000_device_t object (the communication channel) // status_code - two byte (0..255) status code as defined in // fmt - optional additional reply text format (may be NULL) // Returns: // Negative value on error. int cp3000_send_reply(cp3000_device_t, int status_code, const char *fmt, ...); int cp3000_vsend_reply(cp3000_device_t, int status_code, const char *fmt, va_list ap); // Receive a reply // Parameters: // cp3000_device_t object (the communication channel) // status code (pointer to) // msg - text part of the reply (may be NULL) // msg_sz - size of 'msg' // Returns: // Number of bytes in 'msg' or '-1' on error // Remarks: // Fills the putback list, which must be processed int cp3000_recv_reply(cp3000_device_t device, int *status_code, char *msg, size_t msg_sz); int cp3000_recv_reply_for_client(cp3000_device_t device, int client_id, int *status_code, char *msg, size_t msg_sz); // Forward the tokens received // Parameters: // cp3000_device_t object (the communication channel) // tokens to send // Returns: // Negative value on error. int cp3000_forward_tokens(cp3000_device_t, token_info_t tokens); // find the definition of the token returned by 'cp3000_tokenize()' // in the list 'definitions' (the last entry in this list must have // its 'cmd' field set to NULL) const token_definition_t *cp3000_match_token(const token_definition_t *definitions, token_info_t token); /* ** Checksum calculation and checking */ // calculate the checksum of a message int cp3000_checksum(const char *buffer); // check checksum in a message; return '0' when the checksum matches, // '-1' when the message does not contain a checksum or a positive // value when the checksum does not match. int cp3000_match_checksum(const char *buffer); /* ** Wait for data */ // Read data from a CP3000 communication channel, in three steps // 1. Optionally, wait until data is availble using 'cp3000_select()' // 2. Read it and store it in the internal buffer // 3. Read strings from the internal buffer // Wait until data is ready to be read, with optional time-out. // Similar to 'select(2)' (which _could_ be called, for that matter), // but also checks the internal buffers. // OpenSSL safe, as 'select(2)' may not always work as intended. // Parameters: // - An array (with count) of cp3000_device_t's to check. The array // is _changed_ and contains the devices with data available upon // exit. // - Optional time-out if not NULL; when the time-out is specified and // set to 0.0 seconds, the function will return immediately // Returns: // < 0 on error // 0 when the time-out was triggered // - number of entries in the cp3000_device_t; these are the device(s) // that have data available int cp3000_select(cp3000_device_t*, int n_devices, struct timeval*); // Check if a device is in the array upon returning from 'cp3000_wait_data()' int cp3000_in_select(const cp3000_device_t, const cp3000_device_t *devices, int n_devices); /* ** Raw data access ** It is recommended to use the 'tokenizer' functions above */ // Synchronise with the peer, as good as possible // Returns: // 1 on success // 0 or < 0 on failure int cp3000_sync(cp3000_device_t); // Send data over a CP3000 communication channel int cp3000_send_data(cp3000_device_t, const char *data, int len); // Read data from the CP3000 channel into the internal buffer // Returns: // > 0: number of bytes read from the communication channel in // this call // 0: channel was closed // < 0 on error int cp3000_recv_data(cp3000_device_t); // Read a string or binary data from the internal buffer // Returns: // 0 if there is not enough data, i.e. not a full strings worth // > 0: lenght of the string returned in 'buffer' // < 0 on error // Remarks: // The string reader will read upto the next new line and it returns the length // of the string in the buffer. The binary reader will return 'n_bytes' or zero // if the buffer does not contain enough data to satisfy the request. int cp3000_recv_string(cp3000_device_t, char *buffer, int bufsz); int cp3000_recv_bytes(cp3000_device_t, unsigned char *buffer, int n_bytes); // Access to the file descriptors for low level access int cp3000_get_rfd(cp3000_device_t); int cp3000_get_wfd(cp3000_device_t); // Wrappers for FD_SET and FD_ISSET; the parameter 'max' is used to determine // the file descriptor with the greatest number (if fd > max then max := fd;) int cp3000_fd_set(cp3000_device_t, fd_set*, int *max); int cp3000_in_fd_set(cp3000_device_t, fd_set*); /* ** Escape and unescape strings */ // escape quotes and other characters in a string and returns 'dest' or // NULL when 'dest' is too small // - the size of 'dest' is, worst case, four times as great as 'src' const char *cp3000_escape(const char *src, char *dest, size_t dest_sz); // escape binary data const char *cp3000_nescape(const unsigned char *src, size_t src_sz, char *dest, size_t dest_sz); // remove the escapes and returns 'dest' or NULL if 'dest' is too small // - 'src' and 'dest' may overlap // - 'dest' will never be longer than 'src' const char *cp3000_unescape(const char *src, char *dest, size_t dest_sz); /* ** Convert an array of bytes to a string with hexadecimal numbers and v.v. */ // escape an array of bytes (the 'dest' string will contain the hexa- // decimal representation of each byte) and v.v. // - the size of 'dest' must be (2*src_sz + 1) characters // - always returns the number of characters is 'dest' for success int cp3000_bin2hex(const unsigned char *src, size_t src_sz, char *dest); // the reverse function may fail if the destination is not large enough // (in which case this function returns '-1', however 'dest' will be // filled up to 'dest_sz' bytes), otherwise it returns the number of // bytes in 'dest'. int cp3000_hex2bin(const char *src, unsigned char *dest, size_t dest_sz); /* ** Convert a string to an enumeration value or set (bitfield) */ // for both functions, the input array 'members' must be in order, i.e. // the first entry returns enumeration value '0' resp. set value 0x0001; // NULL values can be used as place holders (they are never matched) // convert a string to an enumeration; returning the first match // return '-1' on error int cp3000_str2enum(const char *str, const char **members, int n_members); // convert a string to a set value (bitfield) // 'str' contains zero or more comma separated values that are to be // matched in 'members' unsigned long cp3000_str2set(const char *str, const char **members, int n_members); /* ** Errors */ // get error string for the supplied error code; if this is TCPERR_SOCKET, // then the function will try to retrieve the last error from the C library // or OpenSSL library const char *cp3000_error_string(cp3000_device_t, int error_code); #endif /* __CP3000_H */