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 ;-) .

Keine Kommentare:

Kommentar veröffentlichen