Traces reading of upload data from the application in order to send it to the
server.
+## `ssh`
+
+Tracing of SSH related protocols SCP and SFTP.
+
## `ssls`
Tracing of SSL Session handling, e.g. caching/import/export.
}
#endif /* USE_SSL */
+#ifdef USE_SSH
+struct curl_trc_feat Curl_trc_feat_ssh = {
+ "SSH",
+ CURL_LOG_LVL_NONE,
+};
+
+void Curl_trc_ssh(struct Curl_easy *data, const char *fmt, ...)
+{
+ DEBUGASSERT(!strchr(fmt, '\n'));
+ if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ssh)) {
+ va_list ap;
+ va_start(ap, fmt);
+ trc_infof(data, &Curl_trc_feat_ssh, NULL, 0, fmt, ap);
+ va_end(ap);
+ }
+}
+#endif /* USE_SSH */
+
#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
struct curl_trc_feat Curl_trc_feat_ws = {
"WS",
#ifdef USE_SSL
{ &Curl_trc_feat_ssls, TRC_CT_NETWORK },
#endif
+#ifdef USE_SSH
+ { &Curl_trc_feat_ssh, TRC_CT_PROTOCOL },
+#endif
#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
{ &Curl_trc_feat_ws, TRC_CT_PROTOCOL },
#endif
(void)data; (void)fmt;
}
#endif
+#ifdef USE_SSH
+void Curl_trc_ssh(struct Curl_easy *data, const char *fmt, ...)
+{
+ (void)data;
+ (void)fmt;
+}
+#endif
#ifdef USE_SSL
void Curl_trc_ssls(struct Curl_easy *data, const char *fmt, ...)
{
void Curl_trc_ssls(struct Curl_easy *data,
const char *fmt, ...) CURL_PRINTF(2, 3);
#endif
+#ifdef USE_SSH
+extern struct curl_trc_feat Curl_trc_feat_ssh;
+void Curl_trc_ssh(struct Curl_easy *data,
+ const char *fmt, ...) CURL_PRINTF(2, 3);
+#endif
#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
extern struct curl_trc_feat Curl_trc_feat_ws;
void Curl_trc_ws(struct Curl_easy *data,
do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ssls)) \
Curl_trc_ssls(data, __VA_ARGS__); } while(0)
#endif /* USE_SSL */
+#ifdef USE_SSH
+#define CURL_TRC_SSH(data, ...) \
+ do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ssh)) \
+ Curl_trc_ssh(data, __VA_ARGS__); } while(0)
+#endif /* USE_SSH */
#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
#define CURL_TRC_WS(data, ...) \
do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ws)) \
#ifdef USE_SSL
#define CURL_TRC_SSLS Curl_trc_ssls
#endif
+#ifdef USE_SSH
+#define CURL_TRC_SSH Curl_trc_ssh
+#endif
#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
#define CURL_TRC_WS Curl_trc_ws
#endif
wrong connections. */
char *localdev;
unsigned short localportrange;
- int waitfor; /* current READ/WRITE bits to wait for */
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
int socks5_gssapi_enctype;
#endif
return CURLE_SSH;
}
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
-#define myssh_to(x,y,z) myssh_set_state(x,y,z, __LINE__)
-#else
-#define myssh_to(x,y,z) myssh_set_state(x,y,z)
-#endif
-
-/*
- * SSH State machine related code
- */
-/* This is the ONLY way to change SSH state! */
-static void myssh_set_state(struct Curl_easy *data,
- struct ssh_conn *sshc,
- sshstate nowstate
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- , int lineno
-#endif
- )
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
+static const char *myssh_statename(sshstate state)
{
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- /* for debug purposes */
static const char *const names[] = {
"SSH_STOP",
"SSH_INIT",
"SSH_SESSION_FREE",
"QUIT"
};
+ /* a precaution to make sure the lists are in sync */
+ DEBUGASSERT(CURL_ARRAYSIZE(names) == SSH_LAST);
+ return ((size_t)state < CURL_ARRAYSIZE(names)) ? names[state] : "";
+}
+#else
+#define myssh_statename(x) ""
+#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
+
+#define myssh_to(x,y,z) myssh_set_state(x,y,z)
+
+/*
+ * SSH State machine related code
+ */
+/* This is the ONLY way to change SSH state! */
+static void myssh_set_state(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ sshstate nowstate)
+{
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
if(sshc->state != nowstate) {
- infof(data, "SSH %p state change from %s to %s (line %d)",
- (void *) sshc, names[sshc->state], names[nowstate],
- lineno);
+ CURL_TRC_SSH(data, "[%s] -> [%s]",
+ myssh_statename(sshc->state),
+ myssh_statename(nowstate));
}
-#endif
+#else
(void)data;
+#endif
sshc->state = nowstate;
}
myssh_block2waitfor(conn, sshc, (rc == SSH_AGAIN));
if(rc == SSH_AGAIN) {
- DEBUGF(infof(data, "ssh_connect -> EAGAIN"));
+ CURL_TRC_SSH(data, "connect -> EAGAIN");
}
else if(rc != SSH_OK) {
failf(data, "Failure establishing ssh session");
/* not set by Curl_xfer_setup to preserve keepon bits */
data->conn->recv_idx = FIRSTSOCKET;
- /* store this original bitmask setup to use later on if we cannot
- figure out a "real" bitmask */
- sshc->orig_waitfor = data->req.keepon;
-
/* since we do not really wait for anything at this point, we want the
state machine to move on as soon as possible so we mark this as dirty */
Curl_multi_mark_dirty(data);
}
Curl_safefree(sshp->path);
- DEBUGF(infof(data, "SFTP DONE done"));
+ CURL_TRC_SSH(data, "SFTP DONE done");
/* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT
After nextstate is executed, the control should come back to
we get the homedir here, we get the "workingpath" in the DO action
since the homedir will remain the same between request but the
working path will not. */
- DEBUGF(infof(data, "SSH CONNECT phase done"));
+ CURL_TRC_SSH(data, "CONNECT phase done");
myssh_to(data, sshc, SSH_STOP);
return SSH_NO_ERROR;
}
/* not set by Curl_xfer_setup to preserve keepon bits */
data->conn->recv_idx = FIRSTSOCKET;
- /* store this original bitmask setup to use later on if we cannot
- figure out a "real" bitmask */
- sshc->orig_waitfor = data->req.keepon;
-
myssh_to(data, sshc, SSH_STOP);
break;
ssh_scp_free(sshc->scp_session);
sshc->scp_session = NULL;
}
- DEBUGF(infof(data, "SCP DONE phase complete"));
+ CURL_TRC_SSH(data, "SCP DONE phase complete");
ssh_set_blocking(sshc->ssh_session, 0);
}
if(!result && (sshc->state == SSH_STOP))
result = sshc->actualcode;
- DEBUGF(infof(data, "SSH: myssh_statemach_act -> %d", result));
+ CURL_TRC_SSH(data, "[%s] statemachine() -> %d, block=%d",
+ myssh_statename(sshc->state), result, *block);
return result;
}
static CURLcode myssh_pollset(struct Curl_easy *data,
struct easy_pollset *ps)
{
- int flags = 0;
struct connectdata *conn = data->conn;
- if(conn->waitfor & KEEP_RECV)
- flags |= CURL_POLL_IN;
- if(conn->waitfor & KEEP_SEND)
- flags |= CURL_POLL_OUT;
- if(!conn->waitfor)
- flags |= CURL_POLL_OUT;
- return flags ?
- Curl_pollset_change(data, ps, conn->sock[FIRSTSOCKET], flags, 0) :
- CURLE_OK;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ int waitfor;
+
+ if(!sshc || (sock == CURL_SOCKET_BAD))
+ return CURLE_FAILED_INIT;
+
+ waitfor = sshc->waitfor ? sshc->waitfor : data->req.keepon;
+ if(waitfor) {
+ int flags = 0;
+ if(waitfor & KEEP_RECV)
+ flags |= CURL_POLL_IN;
+ if(waitfor & KEEP_SEND)
+ flags |= CURL_POLL_OUT;
+ DEBUGASSERT(flags);
+ CURL_TRC_SSH(data, "pollset, flags=%x", flags);
+ return Curl_pollset_change(data, ps, sock, flags, 0);
+ }
+ /* While we still have a session, we listen incoming data. */
+ if(sshc->ssh_session)
+ return Curl_pollset_change(data, ps, sock, CURL_POLL_IN, 0);
+ return CURLE_OK;
}
static void myssh_block2waitfor(struct connectdata *conn,
struct ssh_conn *sshc,
bool block)
{
+ (void)conn;
if(block) {
int dir = ssh_get_poll_flags(sshc->ssh_session);
/* translate the libssh define bits into our own bit defines */
- conn->waitfor =
+ sshc->waitfor =
((dir & SSH_READ_PENDING) ? KEEP_RECV : 0) |
((dir & SSH_WRITE_PENDING) ? KEEP_SEND : 0);
}
else
- /* if it did not block, use the original set */
- conn->waitfor = sshc->orig_waitfor;
+ sshc->waitfor = 0;
}
/* called repeatedly until done from multi.c */
if(!sshc || !ssh)
return CURLE_FAILED_INIT;
+ CURL_TRC_SSH(data, "myssh_connect");
if(conn->handler->protocol & CURLPROTO_SCP) {
conn->recv[FIRSTSOCKET] = scp_recv;
conn->send[FIRSTSOCKET] = scp_send;
/* ignore */
}
+ CURL_TRC_SSH(data, "myssh_connect, set socket=%" FMT_SOCKET_T, sock);
rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_FD, &sock);
if(rc != SSH_OK) {
failf(data, "Could not set socket");
result = myssh_multi_statemach(data, dophase_done);
if(*dophase_done) {
- DEBUGF(infof(data, "DO phase is complete"));
+ CURL_TRC_SSH(data, "DO phase is complete");
}
return result;
}
CURLcode result = CURLE_OK;
struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
- DEBUGF(infof(data, "DO phase starts"));
+ CURL_TRC_SSH(data, "DO phase starts");
*dophase_done = FALSE; /* not done yet */
if(!sshc)
*connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
if(*dophase_done) {
- DEBUGF(infof(data, "DO phase is complete"));
+ CURL_TRC_SSH(data, "DO phase is complete");
}
return result;
struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
CURLcode result = CURLE_OK;
- DEBUGF(infof(data, "DO phase starts"));
+ CURL_TRC_SSH(data, "DO phase starts");
*dophase_done = FALSE; /* not done yet */
if(!sshc)
*connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
if(*dophase_done) {
- DEBUGF(infof(data, "DO phase is complete"));
+ CURL_TRC_SSH(data, "DO phase is complete");
}
return result;
{
CURLcode result = myssh_multi_statemach(data, dophase_done);
if(*dophase_done) {
- DEBUGF(infof(data, "DO phase is complete"));
+ CURL_TRC_SSH(data, "DO phase is complete");
}
return result;
}
CURLcode result = CURLE_OK;
(void)dead_connection;
- DEBUGF(infof(data, "SSH DISCONNECT starts now"));
+ CURL_TRC_SSH(data, "DISCONNECT starts now");
if(sshc && sshc->ssh_session) {
/* only if there is a session still around to use! */
result = myssh_block_statemach(data, sshc, sshp, TRUE);
}
- DEBUGF(infof(data, "SSH DISCONNECT is done"));
+ CURL_TRC_SSH(data, "DISCONNECT is done");
return result;
}
Curl_cfree(ptr);
}
-/*
- * SSH State machine related code
- */
-/* This is the ONLY way to change SSH state! */
-static void myssh_state(struct Curl_easy *data,
- struct ssh_conn *sshc,
- sshstate nowstate)
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
+static const char *myssh_statename(sshstate state)
{
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- /* for debug purposes */
- static const char * const names[] = {
+ static const char *const names[] = {
"SSH_STOP",
"SSH_INIT",
"SSH_S_STARTUP",
"SSH_SESSION_FREE",
"QUIT"
};
-
/* a precaution to make sure the lists are in sync */
DEBUGASSERT(CURL_ARRAYSIZE(names) == SSH_LAST);
+ return ((size_t)state < CURL_ARRAYSIZE(names)) ? names[state] : "";
+}
+#else
+#define myssh_statename(x) ""
+#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
+
+#define myssh_state(x,y,z) myssh_set_state(x,y,z)
+
+/*
+ * SSH State machine related code
+ */
+/* This is the ONLY way to change SSH state! */
+static void myssh_set_state(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ sshstate nowstate)
+{
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
if(sshc->state != nowstate) {
- infof(data, "SFTP %p state change from %s to %s",
- (void *)sshc, names[sshc->state], names[nowstate]);
+ CURL_TRC_SSH(data, "[%s] -> [%s]",
+ myssh_statename(sshc->state),
+ myssh_statename(nowstate));
}
-#endif
+#else
(void)data;
+#endif
sshc->state = nowstate;
}
/* not set by Curl_xfer_setup to preserve keepon bits */
data->conn->recv_idx = FIRSTSOCKET;
- /* store this original bitmask setup to use later on if we cannot
- figure out a "real" bitmask */
- sshc->orig_waitfor = data->req.keepon;
-
/* since we do not really wait for anything at this point, we want the
state machine to move on as soon as possible so mark this as dirty */
Curl_multi_mark_dirty(data);
/* in this case, the error was not in the SFTP level but for example a
time-out or similar */
result = CURLE_SSH;
- DEBUGF(infof(data, "error = %lu makes libcurl = %d",
- sftperr, (int)result));
+ CURL_TRC_SSH(data, "error = %lu makes libcurl = %d", sftperr, (int)result);
return result;
}
get the homedir here, we get the "workingpath" in the DO action since the
homedir will remain the same between request but the working path will
not. */
- DEBUGF(infof(data, "SSH CONNECT phase done"));
+ CURL_TRC_SSH(data, "CONNECT phase done");
return CURLE_OK;
}
Curl_safefree(sshp->path);
- DEBUGF(infof(data, "SFTP DONE done"));
+ CURL_TRC_SSH(data, "SFTP DONE done");
/* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT
After nextstate is executed, the control should come back to
/* not set by Curl_xfer_setup to preserve keepon bits */
data->conn->recv_idx = FIRSTSOCKET;
- /* store this original bitmask setup to use later on if we cannot
- figure out a "real" bitmask */
- sshc->orig_waitfor = data->req.keepon;
-
myssh_state(data, sshc, SSH_STOP);
return CURLE_OK;
}
sshc->ssh_channel = NULL;
}
- DEBUGF(infof(data, "SCP DONE phase complete"));
+ CURL_TRC_SSH(data, "SCP DONE phase complete");
myssh_state(data, sshc, SSH_STOP);
break;
*block = TRUE;
result = CURLE_OK;
}
+ CURL_TRC_SSH(data, "[%s] statemachine() -> %d, block=%d",
+ myssh_statename(sshc->state), result, *block);
return result;
}
static CURLcode ssh_pollset(struct Curl_easy *data,
struct easy_pollset *ps)
{
- int flags = 0;
struct connectdata *conn = data->conn;
- if(conn->waitfor & KEEP_RECV)
- flags |= CURL_POLL_IN;
- if(conn->waitfor & KEEP_SEND)
- flags |= CURL_POLL_OUT;
- return flags ?
- Curl_pollset_change(data, ps, conn->sock[FIRSTSOCKET], flags, 0) :
- CURLE_OK;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ int waitfor;
+
+ if(!sshc || (sock == CURL_SOCKET_BAD))
+ return CURLE_FAILED_INIT;
+
+ waitfor = sshc->waitfor ? sshc->waitfor : data->req.keepon;
+ if(waitfor) {
+ int flags = 0;
+ if(waitfor & KEEP_RECV)
+ flags |= CURL_POLL_IN;
+ if(waitfor & KEEP_SEND)
+ flags |= CURL_POLL_OUT;
+ DEBUGASSERT(flags);
+ CURL_TRC_SSH(data, "pollset, flags=%x", flags);
+ return Curl_pollset_change(data, ps, sock, flags, 0);
+ }
+ /* While we still have a session, we listen incoming data. */
+ if(sshc->ssh_session)
+ return Curl_pollset_change(data, ps, sock, CURL_POLL_IN, 0);
+ return CURLE_OK;
}
/*
struct ssh_conn *sshc,
bool block)
{
- struct connectdata *conn = data->conn;
int dir = 0;
+ (void)data;
if(block) {
dir = libssh2_session_block_directions(sshc->ssh_session);
if(dir) {
/* translate the libssh2 define bits into our own bit defines */
- conn->waitfor = ((dir&LIBSSH2_SESSION_BLOCK_INBOUND) ? KEEP_RECV : 0) |
+ sshc->waitfor = ((dir&LIBSSH2_SESSION_BLOCK_INBOUND) ? KEEP_RECV : 0) |
((dir&LIBSSH2_SESSION_BLOCK_OUTBOUND) ? KEEP_SEND : 0);
}
}
if(!dir)
- /* It did not block or libssh2 did not reveal in which direction, put back
- the original set */
- conn->waitfor = sshc->orig_waitfor;
+ sshc->waitfor = 0;
}
/* called repeatedly until done from multi.c */
struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
CURLcode result = CURLE_OK;
- DEBUGF(infof(data, "DO phase starts"));
+ CURL_TRC_SSH(data, "DO phase starts");
*dophase_done = FALSE; /* not done yet */
if(!sshc)
*connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
if(*dophase_done) {
- DEBUGF(infof(data, "DO phase is complete"));
+ CURL_TRC_SSH(data, "DO phase is complete");
}
return result;
result = ssh_multi_statemach(data, dophase_done);
if(*dophase_done) {
- DEBUGF(infof(data, "DO phase is complete"));
+ CURL_TRC_SSH(data, "DO phase is complete");
}
return result;
}
struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
CURLcode result = CURLE_OK;
- DEBUGF(infof(data, "DO phase starts"));
+ CURL_TRC_SSH(data, "DO phase starts");
*dophase_done = FALSE; /* not done yet */
if(!sshc)
*connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
if(*dophase_done) {
- DEBUGF(infof(data, "DO phase is complete"));
+ CURL_TRC_SSH(data, "DO phase is complete");
}
return result;
CURLcode result = ssh_multi_statemach(data, dophase_done);
if(*dophase_done) {
- DEBUGF(infof(data, "DO phase is complete"));
+ CURL_TRC_SSH(data, "DO phase is complete");
}
return result;
}
if(sshc) {
if(sshc->ssh_session) {
/* only if there is a session still around to use! */
- DEBUGF(infof(data, "SSH DISCONNECT starts now"));
+ CURL_TRC_SSH(data, "DISCONNECT starts now");
myssh_state(data, sshc, SSH_SFTP_SHUTDOWN);
result = ssh_block_statemach(data, sshc, sshp, TRUE);
- DEBUGF(infof(data, "SSH DISCONNECT is done -> %d", result));
+ CURL_TRC_SSH(data, "DISCONNECT is done -> %d", result);
}
sshc_cleanup(sshc, data, TRUE);
}
int secondCreateDirs; /* counter use by the code to see if the
second attempt has been made to change
to/create a directory */
- int orig_waitfor; /* default READ/WRITE bits wait for */
+ int waitfor; /* KEEP_RECV/KEEP_SEND bits overriding
+ pollset given flags */
char *slash_pos; /* used by the SFTP_CREATE_DIRS state */
#ifdef USE_LIBSSH