diff options
Diffstat (limited to 'doc/libssh-0.2-api-1.txt')
-rw-r--r-- | doc/libssh-0.2-api-1.txt | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/doc/libssh-0.2-api-1.txt b/doc/libssh-0.2-api-1.txt new file mode 100644 index 0000000..ff35b39 --- /dev/null +++ b/doc/libssh-0.2-api-1.txt @@ -0,0 +1,370 @@ + The new libssh 0.2 API + ---------------------- + +Version 1 + +A. Introduction +--------------- + +With the time from the first release of libssh, I have received lots of +comments about the current API. Myself, I found it quite limiting when doing +my first libssh-server drafts. Thus, I am moving to a stronger API. +This API must still be simple. I am not introducing complex changes. An API +well designed must hide the implementation details. Implementation can change +easily within bugfixes - but API cannot change each release. + +To the people already using libssh 0.11 : sorry. Once I have the complete API +redesigned, I will write a migration paper. It won't be too hard normally. + +Here are the things that were lacking in the previous API and *must* change: + +* A non-blocking mode connection type +* Functions to relegate File descriptor listening to Calling functions and to + the programmer. (I'll explain later). +* Along with that, good buffering system (well, it's not an API but). +* Leave the "functions returns a pointer when it works and NULL when it does + not work". It gives serious problems to implement bindings (A C++ + constructor should not fail and should not depend on a network thing +* Make the Session structure an abstract structure that can work with both + client and *servers*. That mean we should have a Server object which listen + to clients on a bound port, does the different handshakes and return a + session. + Since C is not per se an Object language, I won't use inheritance between + objects. +* This same server thing must provide the reverse capabilities than the + client. That is, accept the handshake, in a nonblocking way. Accept channel + requests, or send them to the controller program. +* Support for program forking : Imagine you have a Ssh server object. You + accept a connection and receive a session, then you receive a channel. You + may want to keep the good old days fork() tricks. Libssh will give a way to + destroy handlers from sessions which belong to an other process without + disturbing the session. +* So often I received the comment back saying that it was not clear why a + session or a channel was terminated. This is over. +* And of course I received lot of mails about the fact I'm doing namespace + polution. this will be resolved this time. +So, please read this draft not as a formal documentation but like a roadmap of +things that each kind of object must do. + +B. Description of objects and functions + +Options structure +~~~~~~~~~~~~~~~~~ + +struct ssh_options *ssh_options_new() + +ssh_options_getopt(options, *argc, argv) + +ssh_options_copy(options) + +char ** ssh_options_get_supported_algos(options,type) + returns a list of the algos supported by libssh, type being one of + SSH_HOSTKEYS, SSH_KEX, SSH_CRYPT, SSH_MAC, SSH_COMP, SSH_LANG + +ssh_options_set_wanted_algos(options,type, char *list) +list being comma-separated list of algos, and type being the upper constants +but with _C_S or _S_V added to them. + +ssh_options_set_port(options, port) + +ssh_options_set_host(options, host) + +ssh_options_set_fd(options, fd) + +ssh_options_set_bind(options, bindaddr, port) +this options sets the address to bind for a client *or* a server. a port of +zero means whatever port is free (what most clients want). + +ssh_options_set_username(options, username) + +ssh_options_set_connect_timeout(options, seconds, usec) + +ssh_options_set_ssh_dir(options, dir) +ssh_options_set_known_hosts_file(options, file) +ssh_options_set_identity(options, file) + +ssh_options_set_banner(options, banner) +ssh_options_allow_ssh1(options, bool allow) +ssh_options_allow_ssh2(options, bool allow) + +options_set_status_callback has moved into ssh_* functions. + +ssh_session Structure +~~~~~~~~~~~~~~~~~~~~~ + +This session structure represents a ssh socket to a server *or* a client. + +ssh_session *ssh_new() + +ssh_set_options(ssh_session,ssh_options) + +ssh_connect(session); + it will return some status describing at which point of the connection it is, + or an error code. If the connection method is non-blocking, the function + will be called more than once, though the return value SSH_AGAIN. + +ssh_set_blocking(session, bool blocking) + set blocking mode or non blocking mode. + +ssh_get_fd(session) + get the currently used connection file descriptor or equivalent (windows) + +ssh_set_fd_toread(session) +ssh_set_fd_towrite(session) +ssh_set_fd_except(session) + Serve to notify the library that data is actualy available to be read on the + file descriptor socket. why ? because on most platforms select can't be done + twice on the same socket when the first reported data to read or to write + +ssh_get_status(session) + Returns the current status bitmask : connection Open or closed, data + pending to read or not (even if connection closed), connection closed on + error or on an exit message + +ssh_get_disconnect_message(session) + Returns the connection disconnect error/exit message + +ssh_get_pubkey_hash(session, hash) + get the public key hash from the server. + +ssh_is_server_known(session) +ssh_write_knownhost(session) + these 2 functions will be kept + +ssh_disconnect(session) + standard disconnect + +ssh_disconnect_error(session,error code, message) + disconnect with a message + +ssh_set_username(session) + set the user name to log in + +ssh_userauth_* functions will be kept as they are now, excepted the fact that +the username field will disapear. +the public key mechanism may get some more functions, like retrieving a public +key from a private key and authenticating without a public key. + +ssh_get_issue_banner(session) + get the issue banner from the server, that is the welcome message. + +ssh_silent_free(session) + This function silently free all data structures used by the session and + closes the socket. It may be used for instance when the process forked and + doesn't want to keep track of this session. This is obviously not possible to + do with separate channels. + +The channel_struct structure +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The channels will change a bit. the constructor thing will change, and the way +to multiplex different connections will change too. channel functions will be +prefixed with "ssh_" + +struct channel_struct *ssh_channel_new() + +ssh_channel_open_session(channel) + will return if the channel allocation failed or not. + +ssh_channel_open_forward(channel, ...) won't change. it will report an error if +the channel allocation failed. + +ssh_channel_send_eof(channel) + send EOF +ssh_channel_close(channel) + closes a channel but doesn't destroy it. you may read unread data still in + the buffer. Once you closed the buffer, the other party can't send you data, + while it could still do it if you only sent an EOF. +ssh_channel_is_closed(channel) + returns true if the channel was closed at one of both sides. a closed chan + may still have data to read, if you closed yourself the connection. otherwise + (you didn't close it) the closed notification only comes when you read the + last buffer byte, or when trying to write into the channel (the SIGPIPE-like + behaviour). + +ssh_channel_is_eof(channel) + reports if the other side has sent an EOF. This functions returns FALSE if + there is still data to read. A closed channel is always EOF. +ssh_channel_free(channel) + completely free the channel. closes it before if it was not done. + +ssh_channel_request_env(channel, name, value) + set an environment variable. + +ssh_channel_request_pty(channel) +ssh_channel_request_pty_size() +ssh_channel_change_pty_size() +ssh_channel_request_shell() +ssh_channel_request_exec() +ssh_channel_request_subsystem() +These functions won't change. + +int ssh_channel_write(channel,data, len,stderr) + Depending on the blocking/non blocking mode of the channel, the behaviour may + change. + stderr is the extended buffer. It's generaly only a server->client stream. + +ssh_channel_set_blocking(bool blocking) + +int ssh_channel_read(channel, buffer, maxlen, is_stderr) + the behaviour will be this one: + -if the chan is in non blocking mode, it will poll what's available to read + and return this. otherwise (nothing to read) it will return 0. + -if the chan is blocking, it will block until at least one byte is + available. +ssh_channel_nonblocking disapears for the later reason. + +int channel_poll(channel, is_stderr) + polls the network and reports the number of bytes ready to be read in the + chan. + +ssh_session ssh_channel_get_session(channel) + returns the session pointer associated to the channel, for simplicity + reasons. + +int ssh_channel_select(CHANNELS *readchans, CHANNELS *writechans, CHANNELS + *exceptchans, struct timeval *timeout) + This function won't work the same way ssh_select did. + I removed the custom file descriptor thing for 2 reasons: + 1- it's not windows compliant. D'ouh ! + 2- most programmers won't want to depend on libssh for socket multiplexing. + that's why i let the programmer poll the fds himself and then use + ssh_set_fd_toread, towrite or except. Then, he may use ssh_channel_select + with a NULL timeout to poll which channels have something to read, write or + error report. + Here is how it's going to work. The coder sets 3 different arrays with the + channels he wants to select(), the last entry being a NULL pointer. The + function will first poll them and return the chans that must be + read/write/excepted. If nothing has this state, the function will select() + using the timeout. + The function will return 0 if everything is ok, SSH_TIMEOUT or SSH_EINTR if + the select was interrupted by a signal. It is dangerous to execute any + channel-related functions into signal handlers. they should set a flag that + you read into your loop. this "trap" (SSH_EINTR) will permit you to catch + them faster and make your program responsive and look fast. + the function will return -1 if a serious problem happens. + + +Error handling +~~~~~~~~~~~~~~ + +when an error happens, the programmer can get the error code and description +with ssh_get_error(session). the creation of a failess constructor for +ssh_session was needed for this reason. + +ssh_get_error_code(session) will return an error code into this subset: + SSH_NO_ERROR : no error :) + SSH_REQUEST_DENIED : you request for a functionality or a service that is not + allowed. The session can continue. + SSH_FATAL : Unrecoverable error. The session can't continue and you should + disconnect the session. It includes the connection being cut without a + disconnect() message. + If a disconnect() message or the channel was closed, a read on such a channel + won't produce an error. otherwise it will return -1 with a SSH_FATAL error + code. + +Server socket binding +~~~~~~~~~~~~~~~~~~~~~ + +It is not possible to bind a socket for ssh with a SSH_SESSION type, because a +single bound port may lead to multiple ssh connections. That's why the +SSH_BIND structure must be created. It uses options from the SSH_OPTIONS +structure. + +SSH_BIND *ssh_bind_new() +creates a structure +ssh_bind_set_options(bind, options) +set the option structure +int ssh_bind_listen(bind) + bind and listen to the port. This call is not blocking. if some error + happens, it returns -1 and the error code can be found with perror(). + +ssh_bind_set_blocking(bind, bool blocking) + should ssh_bind_accept() block or not. + +int ssh_bind_get_fd(bind) + return the bound file descriptor, that is the listener socket. you may put it + into a select() in your code to detect a connection attempt. + +ssh_bind_set_fd_toaccept(bind) + say that the listener socket has a connection to accept (to avoid + ssh_bind_accept() to do a select on it). + +SSH_SESSION *ssh_bind_accept(bind) + return a server handle to a ssh session. if the mode is blocking, the + function will always return a pointer to a session. if the mode is not + blocking, the function can return NULL if there is no connection to accept. + +This SSH_SESSION handle must then pass through the functions explained above. + + +*server functions * + +int ssh_accept(session) + when a new connection is accepted, the handshake must be done. this function + will do the banner handshake and the key exchange. + it will return SSH_AGAIN if the session mode is non blocking, and the + function must be called again until an error occurs or the kex is done. + +Here, I had a few choises about *how* to implement the message parsing as a +server. There are multiple ways to do it, one being callbacks and one being +"Message" reading, parsing and then choice going to the user to use it and +answer. I've choosen the latter because i believe it's the stronger method. +A ssh server can receive 30 different kind of messages having to be dealt by +the high level routines, like channel request_shell or authentication. Having +a callback for all of them would produce a huge kludge of callbacks, with +no relations on when there were called etc. +A message based parsing allows the user to filtrate the messages he's +interested into and to use a default answer for the others. Then, the callback +thing is still possible to handle through a simple message code/callback +function array. + +I did not define yet what it would look like, but i'm sure there will be a +SSH_MESSAGE (they won't have a 1/1 correspondance with ssh packets) which will +be read through +SSH_MESSAGE *ssh_server_read_message(session). +with all of the non-blocking stuff in head like returning NULL if the message +is not full. +Then, the message can be parsed, ie +int ssh_message_get_code(message) +which will return SSH_MESSAGE_AUTH +then +int ssh_message_get_subcode(message) +which then will returh SSH_MESSAGE_AUTH_PASSWORD or _NONE or _PUBKEY etc. + +Then, once the message was parsed, the message will have to be answered, ie +with the generic functions like +ssh_message_accept(message) which says 'Ok your request is accepted' or +ssh_message_deny(message) which says 'Your request is refused'. + +There would be specific message answer functions for some kind of messages +like the authentication one. you may want to reply that the authentication is +Partial rather than denied, and that you still accept some kind of auths, like +ssh_message_auth_reply(message,SSH_AUTH_PARTIAL,SSH_AUTH_PASSWORD | +SSH_AUTH_PUBKEY | SSH_AUTH_KEYBINT); + +I won't let the user have to deal with the channels himself. When a channel is +going to be created by the remote size, a message will come asking to open a +channel. the programmer can either deny or accept, in which case a CHANNEL +object will be created and returned to the programmer. then, all standard +channel functions will run. + +C. Change log of this document + +2. ssh_options_set_username finaly is kept into the options, because it can be +set by ssh_options_getopt() + +1. first release + +D. End notes + +I think libssh must have a very simple to use, powerful and exhaustive API. It +must have no design flaw either. +While I got some good experience at the SSH protocol, I've never writen +more-than-100 lines programs than use libssh and I don't really know the +problems of the library. I'd like people who don't understand some detail into +the API I describe here, who have comments or opinions about it to write me +the soonest possible to limit the damages if I made something the completely +wrong way. +Thanks for your patience. + |