xrdp/libipm/scp.c
matt335672 275eaf7683 Rework transport connect logic
There are a number of ways the existing transport connect logic in
trans_connect could be improved for POSIX compatibility, and also
slightly tidied up:-
1) The same socket is re-used for multiple connect attempts following
   failure which isn't behaviour defined by POSIX.1-2017 (although it
   works on Linux).
2) An asynchronous connect is started, and then after a short
   delay connect() is called again on the same socket. POSIX.1-2017
   is clear that in this situation EALREADY is returned before the
   connection is established, but is silent on the behaviour expected
   when the connection is established. Returning success is an option,
   but so is returning EISCONN. The current code assumes the connect()
   call will succeed.
3) The code contains two virtually identical, quite complex loops for
   TCP and UNIX sockets, differing only in the calls to create a socket
   and connect it.
4) trans_connect() contains looping and retry logic, but this isn't
   seen as sufficient by the chansrv connect code in xrdp/xrdp_mm.c and
   the Xorg connect code in xup/xup.c. Both of these implement their own
   looping and retry logic on top of the logic in trans_connect(),
   resulting in slightly unpredictable behaviour with regard to
   timeouts.
5) A socket number can technically be zero, but in a couple of places
   this isn't allowed for.

This PR attempts to correct the implementation of trans_connect(),
and also to simplify the areas it is called from.

As part of the PR, the signature of the server_is_term member of the
xrdp module interface is changed to match the signature expected by the
is_term member of a struct trans. This allows for trans_connect()
in xrdp modules to directly access g_is_term() within the main xrdp
executable. At the moment this functionality is only used by the xup
module.
2022-03-31 20:48:07 +01:00

497 lines
14 KiB
C

/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Jay Sorg 2004-2022, all xrdp contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
*
* @file libipm/scp.c
* @brief scp definitions
* @author Simone Fedele/ Matt Burt
*/
#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif
#include "scp.h"
#include "libipm.h"
#include "guid.h"
#include "trans.h"
#include "os_calls.h"
#include "string_calls.h"
/*****************************************************************************/
static const char *
msgno_to_str(unsigned short n)
{
return
(n == E_SCP_GATEWAY_REQUEST) ? "SCP_GATEWAY_REQUEST" :
(n == E_SCP_GATEWAY_RESPONSE) ? "SCP_GATEWAY_RESPONSE" :
(n == E_SCP_CREATE_SESSION_REQUEST) ? "SCP_CREATE_SESSION_REQUEST" :
(n == E_SCP_CREATE_SESSION_RESPONSE) ? "SCP_CREATE_SESSION_RESPONSE" :
(n == E_SCP_LIST_SESSIONS_REQUEST) ? "SCP_LIST_SESSIONS_REQUEST" :
(n == E_SCP_LIST_SESSIONS_RESPONSE) ? "SCP_LIST_SESSIONS_RESPONSE" :
NULL;
}
/*****************************************************************************/
const char *
scp_msgno_to_str(enum scp_msg_code n, char *buff, unsigned int buff_size)
{
const char *str = msgno_to_str((unsigned short)n);
if (str == NULL)
{
g_snprintf(buff, buff_size, "[code #%d]", (int)n);
}
else
{
g_snprintf(buff, buff_size, "%s", str);
}
return buff;
}
/*****************************************************************************/
struct trans *
scp_connect(const char *host, const char *port,
int (*term_func)(void))
{
struct trans *t;
if ((t = trans_create(TRANS_MODE_TCP, 128, 128)) != NULL)
{
if (host == NULL)
{
host = "localhost";
}
if (port == NULL)
{
port = "3350";
}
t->is_term = term_func;
trans_connect(t, host, port, 3000);
if (t->status != TRANS_STATUS_UP)
{
trans_delete(t);
t = NULL;
}
else if (libipm_init_trans(t, LIBIPM_FAC_SCP, msgno_to_str) !=
E_LI_SUCCESS)
{
trans_delete(t);
t = NULL;
}
}
return t;
}
/*****************************************************************************/
int
scp_init_trans(struct trans *trans)
{
return libipm_init_trans(trans, LIBIPM_FAC_SCP, msgno_to_str);
}
/*****************************************************************************/
int
scp_msg_in_check_available(struct trans *trans, int *available)
{
return libipm_msg_in_check_available(trans, available);
}
/*****************************************************************************/
int
scp_msg_in_wait_available(struct trans *trans)
{
return libipm_msg_in_wait_available(trans);
}
/*****************************************************************************/
void
scp_msg_in_reset(struct trans *trans)
{
libipm_msg_in_reset(trans);
}
/*****************************************************************************/
enum scp_msg_code
scp_msg_in_start(struct trans *trans)
{
return (enum scp_msg_code)libipm_msg_in_start(trans);
}
/*****************************************************************************/
int
scp_send_gateway_request(struct trans *trans,
const char *username,
const char *password,
const char *connection_description)
{
int rv;
rv = libipm_msg_out_simple_send(
trans,
(int)E_SCP_GATEWAY_REQUEST,
"sss",
username,
password,
connection_description);
/* Wipe the output buffer to remove the password */
libipm_msg_out_erase(trans);
return rv;
}
/*****************************************************************************/
int
scp_get_gateway_request(struct trans *trans,
const char **username,
const char **password,
const char **connection_description)
{
/* Make sure the buffer is cleared after processing this message */
libipm_set_flags(trans, LIBIPM_E_MSG_IN_ERASE_AFTER_USE);
return libipm_msg_in_parse(trans, "sss", username, password,
connection_description);
}
/*****************************************************************************/
int
scp_send_gateway_response(struct trans *trans,
int auth_result)
{
return libipm_msg_out_simple_send(
trans,
(int)E_SCP_GATEWAY_RESPONSE,
"i",
auth_result);
}
/*****************************************************************************/
int
scp_get_gateway_response(struct trans *trans,
int *auth_result)
{
int32_t i_auth_result = 0;
int rv = libipm_msg_in_parse(trans, "i", &i_auth_result);
if (rv == 0)
{
*auth_result = i_auth_result;
}
return rv;
}
/*****************************************************************************/
int
scp_send_create_session_request(struct trans *trans,
const char *username,
const char *password,
enum scp_session_type type,
unsigned short width,
unsigned short height,
unsigned char bpp,
const char *shell,
const char *directory,
const char *connection_description)
{
int rv = libipm_msg_out_simple_send(
trans,
(int)E_SCP_CREATE_SESSION_REQUEST,
"ssyqqysss",
username,
password,
type,
width,
height,
bpp,
shell,
directory,
connection_description);
/* Wipe the output buffer to remove the password */
libipm_msg_out_erase(trans);
return rv;
}
/*****************************************************************************/
int
scp_get_create_session_request(struct trans *trans,
const char **username,
const char **password,
enum scp_session_type *type,
unsigned short *width,
unsigned short *height,
unsigned char *bpp,
const char **shell,
const char **directory,
const char **connection_description)
{
/* Intermediate values */
uint8_t i_type;
uint16_t i_width;
uint16_t i_height;
uint8_t i_bpp;
/* Make sure the buffer is cleared after processing this message */
libipm_set_flags(trans, LIBIPM_E_MSG_IN_ERASE_AFTER_USE);
int rv = libipm_msg_in_parse(
trans,
"ssyqqysss",
username,
password,
&i_type,
&i_width,
&i_height,
&i_bpp,
shell,
directory,
connection_description);
if (rv == 0)
{
*type = (enum scp_session_type)i_type;
*width = i_width;
*height = i_height;
/* bpp is fixed for Xorg session types */
*bpp = (*type == SCP_SESSION_TYPE_XORG) ? 24 : i_bpp;
}
return rv;
}
/*****************************************************************************/
int
scp_send_create_session_response(struct trans *trans,
int auth_result,
int display,
const struct guid *guid)
{
struct libipm_fsb guid_descriptor = { (void *)guid, sizeof(*guid) };
return libipm_msg_out_simple_send(
trans,
(int)E_SCP_CREATE_SESSION_RESPONSE,
"iiB",
auth_result,
display,
&guid_descriptor);
}
/*****************************************************************************/
int
scp_get_create_session_response(struct trans *trans,
int *auth_result,
int *display,
struct guid *guid)
{
/* Intermediate values */
int32_t i_auth_result;
int32_t i_display;
const struct libipm_fsb guid_descriptor = { (void *)guid, sizeof(*guid) };
int rv = libipm_msg_in_parse(
trans,
"iiB",
&i_auth_result,
&i_display,
&guid_descriptor);
if (rv == 0)
{
*auth_result = i_auth_result;
*display = i_display;
}
return rv;
}
/*****************************************************************************/
int
scp_send_list_sessions_request(struct trans *trans,
const char *username,
const char *password)
{
int rv;
rv = libipm_msg_out_simple_send(
trans,
(int)E_SCP_LIST_SESSIONS_REQUEST,
"ss",
username,
password);
/* Wipe the output buffer to remove the password */
libipm_msg_out_erase(trans);
return rv;
}
/*****************************************************************************/
int
scp_get_list_sessions_request(struct trans *trans,
const char **username,
const char **password)
{
/* Make sure the buffer is cleared after processing this message */
libipm_set_flags(trans, LIBIPM_E_MSG_IN_ERASE_AFTER_USE);
return libipm_msg_in_parse(trans, "ss", username, password);
}
/*****************************************************************************/
int
scp_send_list_sessions_response(
struct trans *trans,
enum scp_list_sessions_status status,
const struct scp_session_info *info)
{
int rv;
if (status != E_SCP_LS_SESSION_INFO)
{
rv = libipm_msg_out_simple_send(
trans,
(int)E_SCP_LIST_SESSIONS_RESPONSE,
"i", status);
}
else
{
rv = libipm_msg_out_simple_send(
trans,
(int)E_SCP_LIST_SESSIONS_RESPONSE,
"iiuyqqyxss",
status,
info->sid,
info->display,
info->type,
info->width,
info->height,
info->bpp,
info->start_time,
info->username,
info->connection_description);
}
return rv;
}
/*****************************************************************************/
int
scp_get_list_sessions_response(
struct trans *trans,
enum scp_list_sessions_status *status,
struct scp_session_info **info)
{
int32_t i_status;
int rv;
if (info == NULL)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "Bad pointer in %s", __func__);
rv = 1;
}
else if ((rv = libipm_msg_in_parse(trans, "i", &i_status)) == 0)
{
*status = (enum scp_list_sessions_status)i_status;
struct scp_session_info *p = NULL;
if (*status == E_SCP_LS_SESSION_INFO)
{
int32_t i_sid;
uint32_t i_display;
uint8_t i_type;
uint16_t i_width;
uint16_t i_height;
uint8_t i_bpp;
int64_t i_start_time;
char *i_username;
char *i_connection_description;
rv = libipm_msg_in_parse(
trans,
"iuyqqyxss",
&i_sid,
&i_display,
&i_type,
&i_width,
&i_height,
&i_bpp,
&i_start_time,
&i_username,
&i_connection_description);
if (rv == 0)
{
/* Allocate a block of memory large enough for the
* structure result, and the strings it contains */
unsigned int len = sizeof(struct scp_session_info) +
g_strlen(i_username) + 1 +
g_strlen(i_connection_description) + 1;
if ((p = (struct scp_session_info *)g_malloc(len, 1)) == NULL)
{
*status = E_SCP_LS_NO_MEMORY;
}
else
{
/* Set up the string pointers in the block to point
* into the memory allocated after the block */
p->username = (char *)p + sizeof(struct scp_session_info);
p->connection_description =
p->username + g_strlen(i_username) + 1;
/* Copy the data over */
p->sid = i_sid;
p->display = i_display;
p->type = (enum scp_session_type)i_type;
p->width = i_width;
p->height = i_height;
p->bpp = i_bpp;
p->start_time = i_start_time;
g_strcpy(p->username, i_username);
g_strcpy(p->connection_description,
i_connection_description);
}
}
}
*info = p;
}
return rv;
}