Skip to content

Commit f7afdf4

Browse files
committed
net_imap: Abort if node disconnects in middle of FETCH response.
Previously, the cleanup label in process_fetch did not break out of the loop, so even though we had detected a node disconnect, we would still loop and try to write out the entire FETCH response (which could potentially be quite long). On I/O error, we now correctly abort immediately, as was originally intended.
1 parent 10328cc commit f7afdf4

File tree

2 files changed

+26
-16
lines changed

2 files changed

+26
-16
lines changed

nets/net_imap/imap.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,14 @@ extern int imap_debug_level;
170170
__imap_parallel_reply(imap, fmt, ## __VA_ARGS__); \
171171
}
172172

173-
#define __imap_parallel_reply(imap, fmt, ...) imap_debug(4, "%p <= " fmt, imap, ## __VA_ARGS__); bbs_node_any_fd_writef(imap->node, imap->node->wfd, fmt, ## __VA_ARGS__);
173+
/* Use statement expressions so caller can check the return value of write */
174+
#define __imap_parallel_reply(imap, fmt, ...) ({ imap_debug(4, "%p <= " fmt, imap, ## __VA_ARGS__); bbs_node_any_fd_writef(imap->node, imap->node->wfd, fmt, ## __VA_ARGS__); })
175+
#define _imap_reply_nolock_fd(imap, fd, fmt, ...) ({ imap_debug(4, "%p <= " fmt, imap, ## __VA_ARGS__); bbs_node_fd_writef(imap->node, fd, fmt, ## __VA_ARGS__); })
176+
#define _imap_reply_nolock_fd_lognewline(imap, fd, fmt, ...) ({ imap_debug(4, "%p <= " fmt "\r\n", imap, ## __VA_ARGS__); bbs_node_fd_writef(imap->node, fd, fmt, ## __VA_ARGS__); })
174177

175-
#define _imap_reply_nolock_fd(imap, fd, fmt, ...) imap_debug(4, "%p <= " fmt, imap, ## __VA_ARGS__); bbs_node_fd_writef(imap->node, fd, fmt, ## __VA_ARGS__);
176-
#define _imap_reply_nolock_fd_lognewline(imap, fd, fmt, ...) imap_debug(4, "%p <= " fmt "\r\n", imap, ## __VA_ARGS__); bbs_node_fd_writef(imap->node, fd, fmt, ## __VA_ARGS__);
177-
#define _imap_reply_nolock(imap, fmt, ...) _imap_reply_nolock_fd(imap, imap->node->wfd, fmt, ## __VA_ARGS__);
178+
#define _imap_reply_nolock(imap, fmt, ...) _imap_reply_nolock_fd(imap, imap->node->wfd, fmt, ## __VA_ARGS__)
178179
#define _imap_reply(imap, fmt, ...) _imap_reply_nolock(imap, fmt, ## __VA_ARGS__)
179-
#define imap_send_nocrlf(imap, fmt, ...) _imap_reply_nolock_fd_lognewline(imap, imap->node->wfd, fmt, ## __VA_ARGS__);
180+
#define imap_send_nocrlf(imap, fmt, ...) _imap_reply_nolock_fd_lognewline(imap, imap->node->wfd, fmt, ## __VA_ARGS__)
180181
#define imap_send(imap, fmt, ...) _imap_reply(imap, "%s " fmt "\r\n", "*", ## __VA_ARGS__)
181182
#define imap_reply(imap, fmt, ...) do { \
182183
bbs_assert(!imap->finalized_response); \

nets/net_imap/imap_server_fetch.c

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -776,9 +776,12 @@ static int process_fetch_finalize(struct imap_session *imap, struct fetch_reques
776776
}
777777
}
778778

779-
imap_send_nocrlf(imap, /* Start the IMAP response, and ensure no CR LF is automatically added. */
779+
if (imap_send_nocrlf(imap, /* Start the IMAP response, and ensure no CR LF is automatically added. */
780780
"* %d " /* Number after FETCH is always a message sequence number, not UID, even if usinguid */
781-
"FETCH (%s", seqno, response); /* No closing paren, we do that at the very end */
781+
"FETCH (%s", seqno, response) < 0) { /* No closing paren, we do that at the very end */
782+
res = -1;
783+
goto cleanup;
784+
}
782785

783786
/* Now, send anything that could be a large, variable amount of data.
784787
* All of these will use a format literal / multiline response */
@@ -950,21 +953,21 @@ static int process_fetch(struct imap_session *imap, int usinguid, struct fetch_r
950953
int markseen, recent;
951954

952955
if (entry->d_type != DT_REG || !strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
953-
goto cleanup;
956+
goto next;
954957
}
955958
msguid = imap_msg_in_range(imap, ++seqno, entry->d_name, sequences, usinguid, &error);
956959
if (!msguid) {
957-
goto cleanup;
960+
goto next;
958961
}
959962
if (fetchreq->changedsince) {
960963
if (parse_modseq_from_filename(entry->d_name, &modseq)) {
961-
goto cleanup;
964+
goto next;
962965
}
963966
if (modseq <= fetchreq->changedsince) {
964967
#ifdef EXTRA_DEBUG
965968
bbs_debug(5, "modseq %lu older than CHANGEDSINCE %lu\n", modseq, fetchreq->changedsince);
966969
#endif
967-
goto cleanup; /* Older than specified CHANGEDSINCE */
970+
goto next; /* Older than specified CHANGEDSINCE */
968971
}
969972
}
970973
/* At this point, the message is a match. Fetch everything we're supposed to for it. */
@@ -997,19 +1000,19 @@ static int process_fetch(struct imap_session *imap, int usinguid, struct fetch_r
9971000
*/
9981001
recent = (unsigned int) seqno >= imap->minrecent && (unsigned int) seqno <= imap->maxrecent;
9991002
if (fetchreq->flags && process_fetch_flags(imap, entry->d_name, markseen, recent, response, sizeof(response), &buf, &len)) {
1000-
goto cleanup;
1003+
goto next;
10011004
}
10021005
if (fetchreq->rfc822size && process_fetch_size(entry->d_name, response, sizeof(response), &buf, &len)) {
1003-
goto cleanup;
1006+
goto next;
10041007
}
10051008
if (fetchreq->modseq && process_fetch_modseq(entry->d_name, response, sizeof(response), &buf, &len, &modseq)) {
1006-
goto cleanup;
1009+
goto next;
10071010
}
10081011
if (fetchreq->internaldate && process_fetch_internaldate(fullname, response, sizeof(response), &buf, &len)) {
1009-
goto cleanup;
1012+
goto next;
10101013
}
10111014
if (fetchreq->envelope && process_fetch_envelope(fullname, response, sizeof(response), &buf, &len)) {
1012-
goto cleanup;
1015+
goto next;
10131016
}
10141017

10151018
/* Handle the header/body stuff and actually send the response. */
@@ -1020,10 +1023,16 @@ static int process_fetch(struct imap_session *imap, int usinguid, struct fetch_r
10201023
mark_seen(imap, seqno, fullname, entry->d_name); /* No need to goto cleanup if we fail, we do anyways */
10211024
}
10221025

1026+
next:
10231027
fetched++;
1028+
free(entry);
1029+
continue;
10241030

10251031
cleanup:
1032+
/* For example, if there was an I/O error during the FETCH response, abort immediately.
1033+
* This is not uncommon - a client could exit in the middle of a FETCH 1:* */
10261034
free(entry);
1035+
break;
10271036
}
10281037
free(entries);
10291038
if (!fetched) {

0 commit comments

Comments
 (0)