Montag, 30. Dezember 2013

PuTTY Backend Handling - A Virtual Functions Example - Part 2/2

This is the second and the last part about my digging of the PuTTY sources. The first part introduced the Backend interface which is used to provide all applications of the PuTTY suite (PuTTY terminal GUI, pscp, plink...)  with an abstratction of the communication protocol (ssh, telnet...) choosen by the user.

After looking  at the way PuTTY is organizing the backends available, this post concludes with an example of how the Backend interface is used in application code.

Collecting Backends

 

The available backends are bundled in an array of Backends which is later accessed by convenience functions to select the right Backend for a connection.
// file BE_ALL_S.C (all backends):

Backend *backends[] = {
    &ssh_backend,
    &telnet_backend,
    &rlogin_backend,
    &raw_backend,
    &serial_backend,
    NULL
}
Not all applications in the PuTTY suite require all backends. Hence, there are similar files (modules) to the BE_ALL_S.C module above with just a shorter list of available backends.

The following module BE_SSH.C only contains the Backend for SSH (used in plink and psftp):
// file BE_SSH.C (used in pscp, psftp)

Backend *backends[] = {
    &ssh_backend,
    NULL
}
Applications which let you choose between different backends are mainly using the convenience functions backend_from_proto:
Backend *backend_from_proto(int proto)
{
    Backend **p;
    for (p = backends; *p != NULL; p++)
        if ((*p)->protocol == proto)
         return *p;
    return NULL;
}
This functions takes in a protocol identifier number and then tries to find the right backend struct in an array of available backends (see BE_ALL_S.C and BE_SSH.C ).
For that purpose it is comparing the protocol identifier number with the identifier number configured as second last member of the backend structs. In our examples RAW.C and SSH.C as well as in all other modules which implement a certain protocol, the id number is not coded directly but as more reader friendly constants (e.g. PROT_RAW and PROT_SSH).

A last remark to backend_from_proto: The way the function iterates through the backends is a nice example of pointer arithmetic: Iterator p is declared as the same type as the backends array (remember, arrays are pointers in C). By setting it to the value of the backends array, is pointing to the first element of that array. Incrementing p by one skips over to the next element of the backends array. Since the array is NULL terminated it is easy to check whether or not p has reached the end of the array.

Using The Backend


Now only the actual usage of the Backend interface inside the application code is missing. The next lines of code are snippets from windows.c, the PuTTY main program:
// window.c - example of using the flexible backend
// in application code

static Backend *back;

...

back = backend_from_proto(conf_get_int(conf, CONF_protocol));

...

error = back->init(NULL, &backhandle, conf,
                   conf_get_str(conf, CONF_host),
                   conf_get_int(conf, CONF_port),
                   &realhost,
                   conf_get_int(conf, CONF_tcp_nodelay),
                   conf_get_int(conf, CONF_tcp_keepalives));

back->provide_logctx(backhandle, logctx);

...

Line 3 declares variable back as a pointer to a Backend implementation (well, actually struct as we've seen before). Line 7 let the magic happen: it takes the users selection (ssh, telnet...) and loads via function conf_get_int ( user selection to protocol id number) and eventually function backend_from_proto the right implementation of the communication protocol (see above for details).
In line 11 and 18 contain two examples for working with the backend functions. As can be seen, this is totally independent from the users selection. back->init(...) on line 11 will be processed for an ssh connection as well as a telnet connection (and all other protocols available for user selection).

Here we harvest the fruits of our effort (can one say that in English ?! ;-)  ): The logic of the application is completely decoupled from the actual implementation of the communication protocols.

window.c above is part of the well known Windows version of PuTTY. There is also a lesser known Linux version. There, the implementation of the application (the GUI) part differs from the Windows implementation. However, the Windows and the Linux version of PuTTY share exactelly the same implementation of the communication protocols (our Backends).


Here, our digging of the PuTTY sources ends. I have to admit, the structure and implementation the PuTTY author(s) chose to implement the communication protocols really impressed me - and is now also part of my own arsenal ;-) .

Freitag, 27. Dezember 2013

PuTTY Backend Handling - A Virtual Functions Example - Part 1/2

In one of my past posts I already wrote about more general insights while studing the PuTTY sources. One ill baby and some Chrismas preparations later I'm glad to post two articles about my clues looking at the code of some really experienced guys (well, it is mostly Simon Tatham - the main author of PuTTY).

I particuarly liked the way different applications of the PuTTY suite  (the well known PuTTY SSH GUI is just one amongst others, there is also plink, pscp ...) are sharing the implementation for certain communication protocols. The next screenshot shows where to select those protocols in the PuTTY SSH GUI:

Choosing between different connection protocols in the PuTTY GUI
In this post and the next one I want to show you how nicely the PuTTY authors share the implementation of those communication protocols amongst different tools of the PuTTY suite.

Overview


Before looking at the code lets have a look at this little sketch: (made with Asciiflow)
The implementation of the communication protocols is shared amongst all applications of the PuTTY suite 

This overview should assist you while continue reading. As can be seen, the central header file PUTTY.H contains the definition of the Backend interface.

Interfaces


Short stop: The term interface is widely used in object oriented languages but not so often when it comes to C. However, books like "C Programming: A Modern Approach" or "Test Driven Development for Embedded C" are heavly promoting (particuarly the latter one) the concept of an interface ( a C header file) and its implementation ( a C module). It's nice to see that an old school language like C already contained everything required for this recommendable programming approach.

Back to PuTTY and its sources. From a technical side the Backend interface is a struct mainly consisting out of function pointers. The name of the function pointers are the ones which will be later implemented by the single backend modules (RAW.C, TELNET.C, ...)
// file PUTTY.H

// Defining the backend interface:
struct backend_tag {
    const char *(*init) (void *frontend_handle, void **backend_handle,
    Conf *conf, char *host, int port, char **realhost,
    int nodelay, int keepalive);
    void (*free) (void *handle);
    /* back->reconfig() passes in a replacement configuration. */
    void (*reconfig) (void *handle, Conf *conf);
    /* back->send() returns the current amount of buffered data. */
    int (*send) (void *handle, char *buf, int len);
    /* back->sendbuffer() does the same thing but without attempting a send */
    int (*sendbuffer) (void *handle);
    void (*size) (void *handle, int width, int height);
    void (*special) (void *handle, Telnet_Special code);
    const struct telnet_special *(*get_specials) (void *handle);
    int (*connected) (void *handle);
    int (*exitcode) (void *handle);
    /* If back->sendok() returns FALSE, data sent to it from the frontend
     * may be lost. */
    int (*sendok) (void *handle);
    int (*ldisc) (void *handle, int);
    void (*provide_ldisc) (void *handle, void *ldisc);
    void (*provide_logctx) (void *handle, void *logctx);
    /*
     * back->unthrottle() tells the back end that the front end
     * buffer is clearing.
     */
    void (*unthrottle) (void *handle, int);
    int (*cfg_info) (void *handle);
    char *name;
    int protocol;
    int default_port;
};

// create the "Backend" data type for struct backend_tag"
typedef struct backend_tag Backend;
typedef for a struct is usually combined with the definition of the struct tag but can ( like here ) of course be written separately.

Interface Implementations


Now that we've seen the interface of the Backend lets have a look inside one of the Backend implementations. For simplicity I've chosen module RAW.C.

At the header of the module the raw_backend struct ( I don't dare writting  "object" ;-) ) is declared and initialized with the function names which actually implement the interface. The functions themself are living in the same module file:
// RAW.C - declaring and initializing the raw_backend struct 
// with the names  of the modul's functions which 
// implement the interface in PUTTY.H plus some default values.
...
Backend raw_backend = {
    raw_init,
    raw_free,
    raw_reconfig,
    raw_send,
    raw_sendbuffer,
    raw_size,
    raw_special,
    raw_get_specials,
    raw_connected,
    raw_exitcode,
    raw_sendok,
    raw_ldisc,
    raw_provide_ldisc,
    raw_provide_logctx,
    raw_unthrottle,
    raw_cfg_info,
    "raw",
    PROT_RAW,
    0
}
...
After publishing the names of the functions, the actual implementation follows in the same file, for demonstration, I just give you the stubs of the first two, raw_init and raw_free.
// RAW.C - local module functions (static) containing 
// the implementation of the Backend interface.

static const char *raw_init(void *frontend_handle, void **backend_handle,
       Conf *conf,
       char *host, int port, char **realhost, int nodelay,
       int keepalive)
{
   ...
}


static void raw_free(void *handle)
{
   ...
}
You might share my excitement ;-) when we look at the implementation of the next protocol. This time we're brave and browse through SSH.C, the implementation of interface Backend for the SSH protocol.

First, again the declaration and initialization of the Backend struct:
// SSH.C - the implementation of interface "Backend" 
// for the SSH protocol

Backend ssh_backend = {
    ssh_init,
    ssh_free,
    ssh_reconfig,
    ssh_send,
    ssh_sendbuffer,
    ssh_size,
    ssh_special,
    ssh_get_specials,
    ssh_connected,
    ssh_return_exitcode,
    ssh_sendok,
    ssh_ldisc,
    ssh_provide_ldisc,
    ssh_provide_logctx,
    ssh_unthrottle,
    ssh_cfg_info,
    "ssh",
    PROT_SSH,
    22
}
Again, the actual functions containing the implementation reside in the same file (just the stubs of the first two):
// SSH.C - private functions implementing 
// the interface "Backend"

static const char *ssh_init(void *frontend_handle, void **backend_handle,
       Conf *conf, char *host, int port, char **realhost,
       int nodelay, int keepalive)
{
    ...
}

static void ssh_free(void *handle)
{
    ...
}
Since I'm just interested in the mechanism of interface vs. implementation in C, I omitted the content of the functions - but did you see how the two modules RAW.C and SSH.C are following the same pattern - specified by (again) the interface PUTTY.H.

Now that there is the implementation of the protocols we need to investigate how the actual applications are using them. This is something for part two of this little series.