Hi Martin,
Finally I have managed to implement a wrapper class for putty with SFTP functionality.
I think that SSH v2 is supported by more servers than SSH v1, and almost all servers that supports SSH v1 also supports SSH v2 nowadays, so I have made only SFTP and no SCP implementation.
I linked the files from psftp project except psftp.c/h and sftp.c/h of course.
Then I started refinning the class creating a set of functions to replace the ones in console.c and cmdline.c for providing multisession support to my class and non-interactive login: (verify_ssh_host_key,
old_keyfile_warning, askcipher, etc). Then I removed cmdline and console linkage as I already implemented the basic functions to replace that ones.
All ok and working till now. Putty seems to be designed for multisession but I have found a critical problem:
"The console_get_line problem".
I think the password asking module is a bit crappy. I'm gona explain this:
When you login, the ssh core calls the function pointed by ssh_get_line to provide the password:
if (!ssh_get_line(s->pwprompt, s->password, sizeof(s->password), TRUE));
As I did my own implementation of the function console_get_line, I pointed ssh_get_line to it, so it's called to provide a password to the ssh core.
Well, as passwords are session dependant atributes, it must be stored as a NON-STATIC class member.
When the console_get_line function is called it has no way to know the password for this session as it's not called with the frontend pointer, so you can't do "return ((class*)frontend)->GetLine(...)" as in the from_backend function.
So I made the console_get_line a member method of the wrapper class. As the function must be static in order to be assigned to the ssh_get_line pointer, I made it static. And the big problem comes now: as the function is now static, it can't access non-static members of the class, so the password string atribute has to be static, which will force all session to have the same password.
When the password entry is interactive this works ok, as the password is provided when needed, so the console_get_line function doesn't have to access any class atributes.
I don't understand why the password is not stored in the cfg struct so the ssh core uses it and don't have to call that crappy function, as it does with the login name.
This is a big problem and I don't know how to solve it. I have been looking to the WinSCP code and I have seen that you use a global pointer variable for solving this problem: CurrentSSH. When you are going to init the session you point the variable to the current TSecureShell object, so when the get_line function is called by the ssh core you do this: Result = CurrentSSH->GetPassword(Password);
After the initialization you point the variable back to NULL.
This method works well for your program as the user can't start more than one session at a time, so the CurrentSSH variable doesn't suffer the exclussion problem. As my program is multithreading, I have a critical problem with this, as if two sessions are created at a time, there will be an exclussion problem with the CurrentSSH variable, as one session will point the variable CurrentSSH to its object and if the
other session accesses the same variable for the same purposes before the first session provides the password, the same password will be sent for the two sessions.
The only way I know to solve this is by using semaphores for accessing the critical section (variable CurrentSSH). But this may carry problems and I consider that it's a waste of time to only permit one session init at a time.
The ssh core should call the get password procedure with the frontend parameter in order to be able to do multiple sessions at a time and should be totally session-independant and not global.
This could be done modding the variable ssh_get_line as this:
GLOBAL int (*ssh_get_line) (void *frontend, const char *prompt, char *str, int maxlen, int is_pw);
and adding the ssh->frontend parameter in each call to ssh_get_line within the putty core.
Personally I think that's better to add the frontend parameter to the function than using mutexes, but this leads to a ssh core modiffication and have to be made every time you upgrade the putty core.
If you know another method not based in a global object pointer (CurrentSSH), please help me.
-----------------------------------------------------
Another question not related to the problem beside.
I want to launch a command per session as I told you some posts before. Thank's to you I know there are two ways of doing this:
- specify the command in cfg.remote_cmd (as psftp and plink does)
- leave blank cfg.remote_cmd and start a remote shell
I have been trying the first method with the psftp wrapper class, specifing "notepad" instead of "sftp" in cfg.remote_cmd and it worked, after changing some cfg parameters and only with commands that not produce output to stdout and stderr. For example, notepad works, but a simple batch file with a line "echo Hello" doen't works, the ssh core throws a connection_fatal with this error:
"Unexpected response to shell/command request: packet type 98"
(I think this is related to F-Secure server, but not sure)
With the second method it also works.
Now there is the problem: the commands I need to execute produce output and I need to log the stdout and stderr outputs of the command and show them in the interface.
I have been testing the plink module and I have seen that it logs the stderr and stdout of a command, but it doesn't parse the output in search of escape characters, so that it prints the output strings and also garbage (escape characters not interpreted).
I think I will need to implement another from_backend function for the "Launching" module of the class to process the data in different way that psftp's from_backend does.
Am I wrong about this? So from_backend should differ the session method (sftp and shell) and call different members of the wrapper class in order to process the data received in a sftp connection and the data from a shell.
Is this ok?
Another questions talking about the cfg:
*What are cfg.ssh_subsys and cfg.ssh_subsys2 for? Concretelly I had to change the cfg.ssh_subsys parameter in order to be able to execute the notepad remotelly.
If I assign TRUE to cfg.ssh_subsys it throws the above error. And it works if I assign FALSE.
*What is the use of cfg.remote_cmd2 if there is cfg.remote_cmd?
I know cfg.remote_cmd_ptr is for providing long commands, but what is cfg.remote_cmd2 for?
*When clossing a session all commands and programs launched in that session are killed. Is there any way to prevent closing them? Does this happen with all servers or this is F-Secure related?
P.S: All tests where done against a F-Secure server (ssh2) (not supports ssh1)
Thank you very much for your help. You have helped me a lot and I couldn't do what I have done without your help.
Best regards.
(Take your time to reply, it's a very long post (I'm sorry for that, but you are the only one that I know that can help me))