Fixed Restconf nghttp2 client EPIPE crash

This commit is contained in:
Olof hagsand 2022-05-13 13:34:06 +02:00
parent c3b120b364
commit 7b099a5685
2 changed files with 34 additions and 27 deletions

View file

@ -358,9 +358,11 @@ native_buf_write(char *buf,
er = errno; er = errno;
switch (SSL_get_error(ssl, len)){ switch (SSL_get_error(ssl, len)){
case SSL_ERROR_SYSCALL: /* 5 */ case SSL_ERROR_SYSCALL: /* 5 */
if (er == ECONNRESET) {/* Connection reset by peer */ if (er == ECONNRESET || /* Connection reset by peer */
if (ssl) er == EPIPE) { /* Reading end of socket is closed */
if (ssl){
SSL_free(ssl); SSL_free(ssl);
}
close(s); close(s);
clixon_event_unreg_fd(s, restconf_connection); clixon_event_unreg_fd(s, restconf_connection);
goto ok; /* Close socket and ssl */ goto ok; /* Close socket and ssl */
@ -812,6 +814,7 @@ restconf_http2_process(restconf_conn *rc,
int ret; int ret;
nghttp2_error ngerr; nghttp2_error ngerr;
clicon_debug(1, "%s", __FUNCTION__);
if (rc->rc_exit){ /* Server-initiated exit for http/2 */ if (rc->rc_exit){ /* Server-initiated exit for http/2 */
if ((ngerr = nghttp2_session_terminate_session(rc->rc_ngsession, 0)) < 0){ if ((ngerr = nghttp2_session_terminate_session(rc->rc_ngsession, 0)) < 0){
clicon_err(OE_NGHTTP2, ngerr, "nghttp2_session_terminate_session %d", ngerr); clicon_err(OE_NGHTTP2, ngerr, "nghttp2_session_terminate_session %d", ngerr);
@ -822,7 +825,7 @@ restconf_http2_process(restconf_conn *rc,
if ((ret = http2_recv(rc, (unsigned char *)buf, n)) < 0) if ((ret = http2_recv(rc, (unsigned char *)buf, n)) < 0)
goto done; goto done;
if (ret == 0){ if (ret == 0){
restconf_close_ssl_socket(rc, 1); restconf_close_ssl_socket(rc, 0);
if (restconf_conn_free(rc) < 0) if (restconf_conn_free(rc) < 0)
goto done; goto done;
retval = 0; retval = 0;
@ -839,6 +842,7 @@ restconf_http2_process(restconf_conn *rc,
} }
retval = 1; retval = 1;
done: done:
clicon_debug(1, "%s %d", __FUNCTION__, retval);
return retval; return retval;
} }
#endif /* HAVE_LIBNGHTTP2 */ #endif /* HAVE_LIBNGHTTP2 */

View file

@ -153,7 +153,10 @@ nghttp2_print_headers(nghttp2_nv *nva,
* `nghttp2_session_send()` to send data to the remote endpoint. If * `nghttp2_session_send()` to send data to the remote endpoint. If
* the application uses solely `nghttp2_session_mem_send()` instead, * the application uses solely `nghttp2_session_mem_send()` instead,
* this callback function is unnecessary. * this callback function is unnecessary.
* XXX see buf_write * It must return the number of bytes sent if it succeeds.
* If it cannot send any single byte without blocking,
* it must return :enum:`NGHTTP2_ERR_WOULDBLOCK`.
* For other errors, it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
*/ */
static ssize_t static ssize_t
session_send_callback(nghttp2_session *session, session_send_callback(nghttp2_session *session,
@ -162,24 +165,22 @@ session_send_callback(nghttp2_session *session,
int flags, int flags,
void *user_data) void *user_data)
{ {
int retval = -1; int retval = NGHTTP2_ERR_CALLBACK_FAILURE;
restconf_conn *rc = (restconf_conn *)user_data; restconf_conn *rc = (restconf_conn *)user_data;
int er; int er;
ssize_t len; ssize_t len;
ssize_t totlen = 0; ssize_t totlen = 0;
int s; int s;
SSL *ssl;
int sslerr; int sslerr;
clicon_debug(1, "%s buflen:%zu", __FUNCTION__, buflen); clicon_debug(1, "%s buflen:%zu", __FUNCTION__, buflen);
s = rc->rc_s; s = rc->rc_s;
ssl = rc->rc_ssl;
while (totlen < buflen){ while (totlen < buflen){
if (ssl){ if (rc->rc_ssl){
if ((len = SSL_write(ssl, buf+totlen, buflen-totlen)) <= 0){ if ((len = SSL_write(rc->rc_ssl, buf+totlen, buflen-totlen)) <= 0){
er = errno; er = errno;
sslerr = SSL_get_error(ssl, len); sslerr = SSL_get_error(rc->rc_ssl, len);
clicon_debug(1, "%s errno:;%d sslerr:%d", __FUNCTION__, errno, sslerr); clicon_debug(1, "%s errno:%d sslerr:%d", __FUNCTION__, errno, sslerr);
switch (sslerr){ switch (sslerr){
case SSL_ERROR_WANT_WRITE: /* 3 */ case SSL_ERROR_WANT_WRITE: /* 3 */
clicon_debug(1, "%s write SSL_ERROR_WANT_WRITE", __FUNCTION__); clicon_debug(1, "%s write SSL_ERROR_WANT_WRITE", __FUNCTION__);
@ -187,12 +188,9 @@ session_send_callback(nghttp2_session *session,
continue; continue;
break; break;
case SSL_ERROR_SYSCALL: /* 5 */ case SSL_ERROR_SYSCALL: /* 5 */
if (er == ECONNRESET) {/* Connection reset by peer */ if (er == ECONNRESET || /* Connection reset by peer */
if (ssl) er == EPIPE) { /* Reading end of socket is closed */
SSL_free(ssl); goto done; /* Cleanup in http2_recv() */
close(s);
// XXX clixon_event_unreg_fd(s, restconf_connection);
goto ok; /* Close socket and ssl */
} }
else if (er == EAGAIN){ else if (er == EAGAIN){
/* same as want_write above, but different behaviour on different /* same as want_write above, but different behaviour on different
@ -204,7 +202,7 @@ session_send_callback(nghttp2_session *session,
continue; continue;
} }
else{ else{
clicon_err(OE_RESTCONF, er, "SSL_write %d", er); clicon_err(OE_RESTCONF, er, "SSL_write %d", sslerr);
goto done; goto done;
} }
break; break;
@ -247,7 +245,7 @@ session_send_callback(nghttp2_session *session,
return retval; return retval;
} }
clicon_debug(1, "%s retval:%zd", __FUNCTION__, totlen); clicon_debug(1, "%s retval:%zd", __FUNCTION__, totlen);
return totlen; return retval == 0 ? totlen : retval;
} }
/*! Invoked when |session| wants to receive data from the remote peer. /*! Invoked when |session| wants to receive data from the remote peer.
@ -473,15 +471,14 @@ http2_exec(restconf_conn *rc,
if ((sd->sd_path = restconf_uripath(rc->rc_h)) == NULL) if ((sd->sd_path = restconf_uripath(rc->rc_h)) == NULL)
goto done; goto done;
sd->sd_proto = HTTP_2; /* XXX is this necessary? */ sd->sd_proto = HTTP_2; /* XXX is this necessary? */
if (strcmp(sd->sd_path, RESTCONF_WELL_KNOWN) == 0 if (strcmp(sd->sd_path, RESTCONF_WELL_KNOWN) == 0
|| api_path_is_restconf(rc->rc_h) || api_path_is_restconf(rc->rc_h)
|| api_path_is_data(rc->rc_h)){ || api_path_is_data(rc->rc_h)){
if (restconf_nghttp2_path(sd) < 0) if (restconf_nghttp2_path(sd) < 0)
goto done; goto done;
} }
else{ else{
sd->sd_code = 404; /* not found */ sd->sd_code = 404; /* not found */
} }
if (restconf_param_del_all(rc->rc_h) < 0) // XXX if (restconf_param_del_all(rc->rc_h) < 0) // XXX
goto done; goto done;
@ -882,7 +879,7 @@ error_callback2(nghttp2_session *session,
* @param[in] buf Character buffer * @param[in] buf Character buffer
* @param[in] n Lenght of buf * @param[in] n Lenght of buf
* @retval 1 OK * @retval 1 OK
* @retval 0 Invald request * @retval 0 Invalid request
* @retval -1 Fatal error * @retval -1 Fatal error
*/ */
int int
@ -918,11 +915,15 @@ http2_recv(restconf_conn *rc,
} }
/* sends highest prio frame from outbound queue to remote peer. It does this as /* sends highest prio frame from outbound queue to remote peer. It does this as
* many as possible until user callback :type:`nghttp2_send_callback` returns * many as possible until user callback :type:`nghttp2_send_callback` returns
* * :enum:`NGHTTP2_ERR_WOULDBLOCK` or the outbound queue becomes empty. * :enum:`NGHTTP2_ERR_WOULDBLOCK` or the outbound queue becomes empty.
* @see session_send_callback()
*/ */
clicon_err_reset();
if ((ngerr = nghttp2_session_send(rc->rc_ngsession)) != 0){ if ((ngerr = nghttp2_session_send(rc->rc_ngsession)) != 0){
clicon_err(OE_NGHTTP2, ngerr, "nghttp2_session_send"); if (clicon_errno)
goto done; goto done;
else
goto fail; /* Not fatal error */
} }
retval = 1; /* OK */ retval = 1; /* OK */
done: done:
@ -943,6 +944,7 @@ http2_send_server_connection(restconf_conn *rc)
,{NGHTTP2_SETTINGS_ENABLE_PUSH, 0}}; ,{NGHTTP2_SETTINGS_ENABLE_PUSH, 0}};
nghttp2_error ngerr; nghttp2_error ngerr;
clicon_debug(1, "%s", __FUNCTION__);
if ((ngerr = nghttp2_submit_settings(rc->rc_ngsession, if ((ngerr = nghttp2_submit_settings(rc->rc_ngsession,
NGHTTP2_FLAG_NONE, NGHTTP2_FLAG_NONE,
iv, iv,
@ -956,6 +958,7 @@ http2_send_server_connection(restconf_conn *rc)
} }
retval = 0; retval = 0;
done: done:
clicon_debug(1, "%s %d", __FUNCTION__, retval);
return retval; return retval;
} }