Libcutlass API documentation What follows is a proposed outline for the libcutlass API, with suggested use cases. Give me feedback. PROGRAM FLOW Program flow is basically the following: 1> Create cutlass handle. 2a> Modify handle default values. 2b> Register action-handling functions. 3> Start cutlass threads. 4> Go off and handle your user's input, loop, do what you will. Handlers will be called as appropriate. 5> Shut down. Greater detail on each section follows. text-cutlass will be a reference implementation on how to use these. GLUE ROUTINES Libcutlass uses a central cutlass handle structure to hold all of its data. To create (and initialize) this structure, use cutlass_init() cutlass_t * cutlass_init(); Once this structure is allocated, the program is responsible for modifying default values and registering action handlers. This can be done in any order, and most can even be done after starting the program, but you will want to do these during program startup. cutlass_init() will return a pointer to the cutlass handle on success, or NULL on failure. Current functions for modifying default values are: int cutlass_set_nick(cutlass_t *cut_handle, char *new_nick); cutlass_set_nick will set your desired nickname to the string passed in. Nicks must be less than CUTLASS_NAME_LEN bytes. The default nick is "" (without the quotes), so you probably want to modify this. cutlass_set_nick() will return 0 on success, or -1 on failure. int cutlass_set_port(cutlass_t *cut_handle, uint16_t port); cutlass_set_port will set your local listening port to the number passed in. You must have root credentials to bind privileged ports (and this is not recommended). By default, the listening port is port 8365, aka CUT_DEFAULT_PORT. Passing a zero will mean that cutlass will not open any port locally, and all connections must be outbound. cutlass_set_port() will return 0 on success, or -1 on failure. int cutlass_set_verbose(cutlass_t *cut_handle, int verbose); cutlass_set_verbose sets the verbosity level of error and informational messages. These will be printed to stderr. CUT_QUIET prints no messages, CUT_ERROR prints error messages, and CUT_INFO prints both error and informational messages. Really insane detail can be found at the CUT_DEBUG level. Even more insane detail lives at CUT_SUPAA_DEBUG. Default verbosity CUT_ERROR. cutlass_set_verbose() will return 0 on success, or -1 on failure. int cutlass_set_permission(cutlass_t *cut_handle, cutlass_capability_t capability, int permission); cutlass_set_permission will set your local cutlass instance's capabilities. These are what types of traffic you will accept, and under what circumstances you will accept it. Call this once per type of capability that you want your cutlass client to have. Currently the following capabilities are defined: #define CAN_RECV_MSGS 0x00000001 #define CAN_RECV_FILES 0x00000002 #define CAN_RECV_AUDIO 0x00000004 #define CAN_RECV_VIDEO 0x00000008 /* Not yet implemented */ #define CAN_FORWARD 0x00000010 /* Not yet implemented */ #define CAN_SERVE_DIR 0x00000020 /* Not yet implemented */ #define CAN_SERVE_FILES 0x00000040 /* Not yet implemented */ The valid permission types are defined: #define NOT_ALLOWED 0 #define USER_ALLOW_CHECK 1 #define ALL_ALLOWED 2 NOT_ALLOWED is pretty much the same thing as not setting the permission at all. USER_ALLOW_CHECK requires the client to have set an action handler for the action (CUT_FILE_OFFER and CUT_SND_OFFER for CAN_RECV_FILES and CAN_RECV_AUDIO, respectively), and ALL_ALLOWED just permits whatever. (Very dangerous for CAN_RECV_FILES). cutlass_set_permission() returns 0 on success, -1 on failure. int cutlass_set_actionlock(cutlass_t *cut_handle, int actionlock); cutlass_set_actionlock will set whether or not you can receive action- handling calls while you are still in an action handler. This is set to 1 (only one action at a time) by default. cutlass_set_actionlock() returns 0 on success, -1 on failure. In addition to modifying default values for cutlass, in order to do anything useful, the program using libcutlass must register action handling functions. Action handlers are functions of the following type: typedef int (*cut_action_handler)(cutlass_t *cut_handle, struct cut_action_obj *action_object); (struct cut_action_obj will be defined below). Once you have written your handler functions, register them using the cutlass_register_action function: int cutlass_register_action(cutlass_t *cut_handle, int action, cut_action_handler *handle_function); cutlass_register_action() returns 0 on success, -1 on failure. The following action handlers may be registered: CUT_USER_CONN_REQ - A remote user would like to connect to you CUT_USER_CONN - A remote user has connected to you CUT_USER_DROP - A remote user has disconnected the connection CUT_USER_TIMEOUT - A remote user has dropped due to timeout CUT_GRP_JOIN - A remote user has joined a group CUT_GRP_JOIN_REQ - A remote user has requested to join a group CUT_GRP_INVITE - You have been invited to a group CUT_GRP_LEAVE - A remote user has left a group CUT_MSG_RECV - You have received a remote text message CUT_FILE_OFFER - A remote user is offering you a file CUT_FILE_REQ - A remote user is requesting a file from you CUT_FILE_ACCEPT - A remote user has accepted your file offer CUT_FILE_REJECT - A remote user has rejected your file offer CUT_FILE_ABORT - An existing file tranfer has been aborted CUT_FILE_SEND_DONE - A file send transfer has been completed CUT_FILE_RECV_DONE - A file receive transfer has been completed CUT_SND_OFFER - A remote user is offering an audio connection CUT_SND_ACCEPT - An audio connection has been established CUT_SND_REJECT - An audio connection has been rejected CUT_SND_DONE - An existing audio connection has been terminated CUT_SYS_MSG - Libcutlass has generated an internal message Action handlers are documented in greater detail in action_handler_guide.txt The struct cut_action_obj should be considered opaque to the libcutlass-using program, but I'll tell you what's in there anyways. struct cut_action_obj { void *cut_handle; uint8_t conn_fingerprint[CUT_ID_LEN]; int action_type; /* CUT_USER_DROP, etc */ char remote_nick[CUTLASS_NAME_LEN]; struct sockaddr_in remote_addr; char group_nick[CUTLASS_NAME_LEN]; cutlass_keyset remote_keyset; cutlass_keyset group_keyset; char file_name[CUTLASS_FILENAME_LEN]; uint8_t file_csum[CUT_CSUM_LEN]; uint8_t external_channel_id; uint32_t file_size; char msg[CUT_MSG_LEN_MAX]; }; Note that not all fields will necessarily be set on all actions, and I may decide to save memory by sticking them into a union or something, so please use the extraction functions to retrieve. uint8_t * cutlass_action_fingerprint (struct cut_action_obj *action_object); char * cutlass_action_rnick(struct cut_action_obj *action_object); char * cutlass_action_gnick(struct cut_action_obj *action_object); cutlass_keyset * cutlass_action_rkey(struct cut_action_obj *action_object); cutlass_keyset * cutlass_action_gkey(struct cut_action_obj *action_object); char * cutlass_action_fname(struct cut_action_obj *action_object); uint8_t * cutlass_action_fcsum(struct cut_action_obj *action_object); uint32_t cutlass_action_fsize(struct cut_action_obj *action_object); char * cutlass_action_msg(struct cut_action_obj *action_object); struct in_addr * cutlass_action_addr(struct cut_action_obj *action_object); int cutlass_action_type(struct cut_action_obj *action_object); int cutlass_action_channel_id(struct cut_action_obj *action_object); Note that while action_object structures will go away after the action handler returns, extracted objects will not, and it is your responsibility to free them. Note that if you try and extract an inappropriate data type (Such as trying to extract a groupname from a CUT_FILE_ACCPT), you'll get a NULL. CRYPTO ROUTINES In order to perform key exchanges and similiar crap, you need the following stuff: typedef void (*callback_fn)(void*); cutlass_private_key generate_rsa_key(uint32_t size_in_bits, callback_fn callback, void* callback_arg); cutlass_private_key is a struct with some opaque stuff in it (a void* to be precise). Use 1024 bits or more. If you use less than 512 bits, a) the function will fail, and b) I will track you down and punch you in the neck. The callback_fn will be called with callback_arg periodically throughout the key generation process (allowing for GUI redraws and so forth). You free the cutlass_private_key by passing it to: void free_private_key(cutlass_private_key); You can store the private key to a file with: void save_private_key(cutlass_private_key, const char* passphrase, FILE* out); Which encrypts the key with the passphrase and stores it into the file. If you don't want a passphrase (naughty!) pass NULL. Passing an empty string will result in a key encrypted with the empty string, rather than an unencrypted key. To read a key that you previously generated and wrote out, use this stuff: typedef int (*passphrase_func)(const char* description, char pass[], uint32_t max_size); cutlass_private_key load_private_key(const char* a_file, passphrase_func); This will return the private key that was loaded. If the key is encrypted, it will call passphrase_func with a description. This function should attempt to get a passphrase for this key from the user somehow, and put it into the char buffer, which has a max size of (surprise) max_size (currently fixed at 128, which should be sufficient). If it returns !0, that means the user cancelled the action and we should stop. Otherwise (returning 0), try the passphrase. If abort. Normally Botan quits out after 3 times, the problem being there is no good way to signal errors in this C-wrapper-around-C++ interface. Probably this will be fixed in the future, assuming I get off my lazy ass and fix it. On failure, load_private_key will return a cutlass_private_key with an internal key value of 0. Thus cutlass_private_key x = load_private_key(some_file, my_passphrase_func); if(x.key == 0) er_ror(); is the right way to check for a failure. IMPORTANT: the private key must be set on the library handle with cut_handle.local_key = our_loaded_private_key; before performing handshaking. This may be useful for interface code: char* fingerprint_key(cutlass_public_key); Which returns a malloc'ed string which contains the fingerprint of the key, to be specific the SHA-1 hash of the BER encoded representation of the key in question. Make sure to free it, or the memory allocation monsters will get you. There are a bunch of other crypto functions, but most of them are only used during key exchange and UI code generally doesn't need to worry about them. CONNECTION ROUTINES So you're set! Your defaults have been set, your actions have been handled (or not, which is OK too, if you don't support sound, or file transfer, or whatnot). Now it's time to kick things off! int cutlass_start(cutlass_t *cut_handle) cutlass_start returns 0 on success, -1 on failure. If this returns successfully, then your handlers will start getting called, and you can start using the following functions to do your own connections'n'stuff: int cutlass_connect(cutlass_t *cut_handle, struct sockaddr *dest_addr); cutlass_connect returns 0 on success, -1 on failure. cutlass_connect will attempt to create a new connection outbound. It will return pretty quickly, but you won't actually get the connection until after kex is complete, which could take whole milliseconds! (The horror!) You will be notified when it is complete via the CUT_USER_CONN action handler. int cutlass_conn_shutdown(cutlass_t *cut_handle, uint8_t *fingerprint) cutlass_conn_shutdown returns 0 on success, -1 on fatal failure, 1 on nonfatal failure. (Nonfatal means you can keep going. fatal means you'd best shut the program down, and states are not guaranteed). cutlass_conn_shutdown will cleanly shutdown the remote connection, and delete it from the hash table when the shutdown has been acknowledged. int cutlass_conn_list(cutlass_t *cut_handle, uint8_t *fingerprint_array, int arr_size); cutlass_conn_list will fill out conn_array with existing connection objects. You are responsible for allocating enough memory, and arr_size is the number of CUT_ID_LEN length objects you can fit. So if you wanted to hold up to 128 connections, alloctae an array of 128 * CUT_ID_LEN, and pass in 128 as your arr_size. It will return the number of array slots filled, between 0 and arr_size. Returns -1 on error. poll_t * cutlass_transport_poll(cutlass_t *cut_handle, uint8_t *fingerprint, int channel_id) cutlass_transport_poll is a method of polling an existing transport channel to see what the status of the underlying transfer is. The channel_id you pass in is the same one returned by cutlass_file_offer(), defined below. The poll_t structure is currently defined as follows: struct transport_poll_obj { int total_bytes; /* Total size of transfer */ int sent_bytes; /* Bytes successfully transferred as of right now */ uint8_t channel_type; /* What kind of transport it is, i.e. CHANNEL_FILE */ uint8_t channel_state; /* What state the transport is in */ void *user_obj; /* A user-defined object, for future use */ }; typedef struct transport_poll_obj poll_t; cutlass_transport_poll() will return NULL on failure. MESSAGE ROUTINES int cutlass_msg_send(cutlass_t *cut_handle, uint8_t *fingerprint, char *message); cutlass_msg_send will send a text message to the remote user corresponding to . This function returns the channel number on success, or -1 on failure. int cutlass_msg_groupcast(cutlass_t *cut_handle, struct cutlass_group *group, char *message); cutlass_msg_groupcast will send a message to all members of group . THIS API WILL CHANGE SHORTLY! (Not yet implemented) int cutlass_msg_broadcast(cutlass_t *cut_handle, char *message); cutlass_msg_broadcast will send a message to all existing remote connections. Returns 0 on success, -1 on fatal error, 1 on nonfatal error. FILE ROUTINES int cutlass_file_offer(cutlass_t *cut_handle, uint8_t *fingerprint, char *file_path); cutlass_file_offer will present the file located at to the remote user corresponding to . The remote user will either have a dialogue box pop up, or it will automatically load the file into the saved files directory. This function returns the channel number on success, or -1 on failure. int cutlass_file_request(cutlass_t *cut_handle, uint8_t *fingerprint, char *file_path); cutlass_file_request will request the file located at from the remote connection corresponding to . The remote user will either have a dialogue box pop up, or return a not found, or it will automatically start sending the file, depending on settings. NOT YET IMPLEMENTED!!! int cutlass_file_accept(cutlass_t *cut_handle, struct cut_action_obj *action_object, char *local_name); cutlass_file_accept can be called by an action handler (presumably the CUT_FILE_OFFR or CUT_FILE_REQ action handler) to indicate that the offered file has been accepted. If the file is being saved under a new name, place the new name into , otherwise, pass NULL as the local_name and the name in the action object will be used (sanitized so that the path becomes the working directory, though). Returns 0 on success, -1 on fatal error, or 1 on nonfatal error. int cutlass_file_reject(cutlass_t *cut_handle, struct cut_action_obj *action_obj); cutlass_file_reject can be called by an action handler (presumably the CUT_FILE_OFFR action handler) to reject the offered file. This is polite, as the channel will stick around half-open until you accept or reject the file. Returns 0 on success, -1 on fatal error, or 1 on nonfatal error. int cutlass_file_cancel(cutlass_t *cut_handle, uint8_t *fingerprint, struct file_transit_info *file_info); cutlass_file_cancel is used to terminate an ongoing file transfer. cutlass_file_cancel is NOT YET IMPLEMENTED!! TERMINATION ROUTINES void cutlass_shutdown_all(cutlass_t *cut_handle); cutlass_shutdown_all terminates all existing connections, and gracefully brings existing threads to a halt. Please call this before exiting. AUDIO ROUTINES int cutlass_audio_connect(cutlass_t *cut_handle, uint8_t *fingerprint); cutlass_audio_connect is the method that external callers use in order to open a connection to a remote (but already connected) peer. Note that just because the call is made, you don't necessarily have working audio yet. You will be notified via an action handler when audio initialization is completed. Returns 0 on success, -1 on fatal error, 1 on nonfatal error. int cutlass_audio_connect_all(cutlass_t *cut_handle); cutlass_audio_connect_all calls cutlass_audio_connect() on every existing connection. You probably shouldn't do this in real life, but it makes for easy (and lazy) debugging. Returns 0 on success, -1 on fatal error, 1 on nonfatal error. int cutlass_audio_disconnect(cutlass_t *cut_handle, uint8_t *fingerprint); cutlass_audio_disconnect disconnects the existing audio stream from the connection specified by the fingerprint. Note that this is just tearing down the audio, not deleting the underlying connection. Of course, if you delete the underlying connection (with cutlass_conn_shutdown()), the audio will go away automatically, too. Returns 0 on success, -1 on fatal error, 1 on nonfatal error. int cutlass_audio_disconnect_all(cutlass_t *cut_handle); cutlass_audio_disconnect_all disconnects all audio connections currently in existence. This is functionally very equivalent to calling cutlass_audio_shutdown(), but it achieves the results in a different way. This makes it useful for debugging cutlass_audio_disconnect(). Returns 0 on success, -1 on fatal error, 1 on nonfatal error. int cutlass_audio_accept(cutlass_t *cut_handle, struct cut_action_obj *action); cutlass_audio_accept should be called in response to an incoming cutlass-audio request message, from within the CUT_SND_OFFER action handler. It means that you are ready to accept the connection and go to town. Returns 0 on success, -1 on fatal error, 1 on nonfatal error. int cutlass_audio_reject(cutlass_t *cut_handle, struct cut_action_obj *action); cutlass_audio_accept should be called in response to an incoming cutlass-audio request message, from within the CUT_SND_OFFER action handler. It means that you have refused the connection. Returns 0 on success, -1 on fatal error, 1 on nonfatal error. void cutlass_audio_shutdown(cutlass_t *cut_handle); cutlass_audio_shutdown closes down all current audio activity. It will notify remote sides that it is terminating audio, and then clean up all data structures on its side. Use this when quitting out, or when disabling all audio activity. Returns 0 on success, -1 on fatal error, 1 on nonfatal error. DIRECTORY ROUTINES int cutlass_directory_advertise(cutlass_t *cut_handle, uint8_t *dir_fprint) cutlass_directory_advertise will notify the remote directory server located at the connectiond that it should advertise your presence to those who request it. int cutlass_directory_unadvertise(cutlass_t *cut_handle, uint8_t *dir_fprint) cutlass_directory_unadvertise will notify the remote directory server located at the connectiond that it should stop advertising your presence int cutlass_directory_advertise_group(cutlass_t *cut_handle, uint8_t *dir_fprint, uint8_t *group_fprint) cutlass_directory_advertise_group will notify the remote directory server located at the connectiond that it should advertise the group identified by . int cutlass_directory_unadvertise_group(cutlass_t *cut_handle, uint8_t *dir_fprint, uint8_t *group_fprint) cutlass_directory_unadvertise_group will notify the remote directory server located at the connectiond that it should stop advertising the group identified by . int cutlass_directory_lookup_print(cutlass_t *cut_handle, uint8_t *dir_fprint, uint8_t *lookup_fprint) cutlass_directory_lookup_print will request info regarding the fingerprint . Information will be returned through an action handler, asynchronously. int cutlass_directory_lookup_regex(cutlass_t *cut_handle, uint8_t *dir_fprint, char *lookup_regex) cutlass_directory_lookup_regex will request info regarding all users whose names match . Information will be returned through an action handler, asynchronously. int cutlass_directory_subscribe(cutlass_t *cut_handle, uint8_t *dir_fprint, uint8_t *lookup_fprint) cutlass_directory_subscribe requests that any time a change in status for fingerprint occurs, that the directory server notify you via an action handler. GROUP ROUTINES THESE APIS WILL BE CHANGING SHORTLY. DO NOT WRITE TO THEM. The following functions are used to administer/create/join groups. group_t * cutlass_group_create(cutlass_t *cut_handle, char *group_name); void cutlass_group_destroy(cutlass_t *cut_handle, group_t *group); int cutlass_group_invite(cutlass_t *cut_handle, group_t *group, conn_t *conn); int cutlass_group_kick(cutlass_t *cut_handle, group_t *group, conn_t *conn); int cutlass_group_ban(cutlass_t *cut_handle, group_t *group, conn_t *conn); int cutlass_group_join(cutlass_t *cut_handle, conn_t *conn, uint8_t *group_id); int cutlass_group_leave(cutlass_t *cut_handle, group_t *group); int cutlass_group_invite_accept(cutlass_t *cut_handle, group_t *group); int cutlass_group_invite_reject(cutlass_t *cut_handle, group_t *group); RETURN VALUES All functions return 0 or a pointer on success, and -1 or NULL on failure, unless otherwise specified. USERCONF ROUTINES cutlass_options* new_options(void); Create a new options struct. It's opaque, don't look. Returns NULL on error. This is useful when initializing a new options file. cutlass_options* load_options(const char* filename); Load a set of options from a file. Will return NULL on error. void save_options(cutlass_options*, const char* filename); Save the options to the file. Returns 0 on success, -1 on error. void delete_options(cutlass_options*); Delete the options. Do this when you're done, otherwise you'll get a nice memory leak void set_int_option(cutlass_options* opt, const char* option_name, int value); void set_str_option(cutlass_options* opt, const char* option_name, const char* value); Set options, integer or string types. int get_int_option(cutlass_options*, const char* option_name); const char* get_str_option(cutlass_options*, const char* option_name); Get the option values, integer or string types. get_str_option will return NULL if the option is not set, get_int_option will return -1.