✓ProveIt
Examples / Report #4022568 Saved analysis

CVE-2026-92389: TFTP direction confusion

curl · 2026-09-1122.2s analysis · $0.028 in model calls

What the report claims

curl's TFTP state machine accepts either direction's opening response regardless of which one was requested, allowing a malicious server to flip the client into transmit mode and drain data from a prior transfer.

Compare with curl's recorded outcome

Known vulnerability

A real vulnerability: CVE-2026-92389 TFTP direction confusion

Shown for comparison after analysis. The pipeline did not see this label.

ProveIt's finding

Needs human review

The available evidence does not settle the report. A maintainer should investigate.

Judgment withdrawn. The answer failed the evidence check.
See the withdrawn model reasoning

The following reasoning failed the citation check and does not support the final finding.

  • tftp_send_first lacks direction checks for TFTP_EVENT_ACK and TFTP_EVENT_DATA (lines 775-781), while the OACK case correctly checks data->state.upload (lines 766-773).
  • A malicious server can answer RRQ with ACK(0), forcing curl into transmit mode (tftp_connect_for_tx) without any upload request.
  • In transmit mode, tftp_tx calls Curl_client_read (line 433), which reads from the sticky CURLOPT_READDATA/infilesize left by a prior transfer (e.g., HTTP POST).
  • The attack requires only ordinary handle reuse across an HTTP POST and a TFTP download — no TFTP-specific options.
  • The HMTFTP draft confirms ACK(0) from the server is not a valid first response to RRQ; the server must send OACK first.
How this analysis ran 5 claims · 12 citations verified

Evidence check: judge quoted an external source that does not say that (https://www.ietf.org/archive/id/draft-maurette-hmtftp-06.html)

Checked against curl master on 2026-09-11 (73d15fcb1a7d). Dev evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.

1 · Grounding
partially grounded
no model · 27 references checked out, 1 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
5 claims · 3792 tokens · $0.0003
3 · Evidence
12 code snippets
real curl source at 73d15fcb1a7d · 3 docs via Tavily
4 · Judgment
Nemotron-3-Ultra-550b-a55b
21419 tokens · 19.4s · $0.0280
5 · Receipts
12 of 12 citations verified
every citation must point at code the judge was shown
Inspect the withdrawn draft reply

This draft belongs to the withdrawn judgment. It may contain unsupported claims.

Thank you for the detailed report and PoC. The code confirms that tftp_send_first() accepts ACK and DATA as first replies without verifying the transfer direction (lines 775-781), while the OACK case does check data->state.upload (lines 766-773). A malicious server answering RRQ with ACK(0) forces curl into transmit mode, which then reads from the sticky CURLOPT_READDATA/infilesize. This is a valid confidentiality issue reachable via ordinary handle reuse. We will apply a fix adding the missing direction checks and a regression test.

Inspect all claims and source code 5 claims · 12 code excerpts

This model judgment was withdrawn. The claim assessments below are preserved for inspection and may be wrong.

Report claims

C1flaw supported

tftp_send_first() dispatches the peer's first reply without checking whether the direction matches what was requested

Read the reasoning

The OACK case (lines 766-773) checks data->state.upload to decide between transmit and receive. The ACK case (line 775-777) and DATA case (line 779-781) lack this check, so a peer's first reply can force the wrong direction.

C2location supported

case TFTP_EVENT_ACK and case TFTP_EVENT_DATA in lib/tftp.c

Read the reasoning

The two cases cited are present in tftp_send_first at the lines shown.

C3trigger supported

A malicious TFTP server can answer an RRQ with ACK block 0

Read the reasoning

When the client sends RRQ (download), a server reply of ACK block 0 produces TFTP_EVENT_ACK, which the state machine treats as 'connected for transmit' and enters tftp_tx, which reads from the upload source.

C4impact supported

curl flips into transmit mode and drains whatever read source the handle's sticky CURLOPT_READDATA points at

Read the reasoning

Once in transmit mode, tftp_tx calls Curl_client_read (line 433), which reads from the sticky read source (CURLOPT_READFUNCTION/CURLOPT_READDATA) bounded by the sticky infilesize (set by prior CURLOPT_POSTFIELDS or left at -1).

C5reproduction supported

run: sh reproduce.sh readdata

Read the reasoning

The PoC demonstrates the attack: HTTP POST sets CURLOPT_READDATA/infilesize, then TFTP download (RRQ) is answered with ACK(0), causing curl to drain the staged file.

Deterministic checks that failed

  • quote 2 of 4 quoted lines appear nowhere in curl, e.g. 'case TFTP_EVENT_ACK: /* only valid reply to WRQ — no upload '

What the report says about other projects

confirmed ACK block 0 is the correct reply only to WRQ; DATA block 1 only to RRQ

“For RRQ: the client sends ACK(0) after receiving OACK, then the server starts with DATA(1). For WRQ: the client starts with DATA(1) after receiving OACK, and the server acknowledges each block with ACK(n).” source

Read the original report (excerpt)

Full report on HackerOne ↗

Hi all, curl's TFTP state machine accepts either direction's opening response regardless of which one was requested. A malicious TFTP server can answer an RRQ with ACK block 0, which flips curl into transmit mode and causes it to drain whatever read source the handle's sticky `CURLOPT_READDATA` points at, including data staged for a completely unrelated transfer. Ordinary handle reuse across an HTTP POST and a TFTP download is sufficient; no TFTP-specific option is required. ## The bug `tftp_send_first()` dispatches the peer's first reply without checking whether the direction matches what was requested: ```c // lib/tftp.c:766-781 case TFTP_EVENT_ACK: /* only valid reply to WRQ — no upload check */ result = tftp_connect_for_tx(state, event); break; case TFTP_EVENT_DATA: /* only valid reply to RRQ — no upload check */ result = tftp_connect_for_rx(state, event); break; ``` The OACK branch above these checks `data->state.upload`. These two don't. Per RFC 1350, ACK block 0 is the correct reply only to WRQ; DATA block 1 only to RRQ. A peer answering an RRQ with ACK-0 flips curl into transmit mode unconditionally. ## Impact path Once in transmit, `tftp_tx()` calls `Curl_client_read()`, falling back to `data->state.fread_func`/`data->state.in` (`lib/sendf.c:1152-1179`) — i.e. `CURLOPT_READFUNCTION`/`CURLOPT_READDATA`. These are sticky options per `curl_easy_setopt(3)`. An earlier POST or MIME transfer on the same handle leaves a read source configured that the application never aimed at the TFTP transfer. A malicious peer drains it. Volume is bounded by sticky `infilesize`: a prior `CURLOPT_POSTFIELDS` body sets it to that body's length; a prior MIME body leaves it at `-1`, meaning drain to EOF. Measured on `curl-8_22_0` and current `main` (staged file: `SECRET-STAGED-FILE-CONTENT`, 27 bytes): ``` mode=readdata http_rc=0 tftp_rc=0 drained=16 capture=[SECRET-STAGED-FI] mode=reset http_rc=0 tftp_rc=0 drained=0 capture=[] mode=conforming http_rc=0 tftp_rc=0 drained=0 capture=[] ``` Both transfers return `CURLE_OK`. The 16-byte bound is the HTTP step's `CURLOPT_POSTFIELDS` length still set as `infilesize` — a length from one unrelated transfer bounding content from another. `reset` and `conforming` (honest peer) both drain nothing, isolating the cause to the peer's wrong-direction reply. No redirect path: `CURLPROTO_REDIR` covers HTTP/HTTP …

Source code

lib/tftp.clines 651–806 · lines 766-781 cited by the report; definition of tftp_send_first
651static CURLcode tftp_send_first(struct tftp_conn *state,
652 tftp_event_t event)
653{
654 size_t sbytes;
655 ssize_t senddata;
656 const char *mode = "octet";
657 char *filename;
658 struct Curl_easy *data = state->data;
659 const struct Curl_sockaddr_ex *remote_addr = NULL;
660 CURLcode result = CURLE_OK;
661
662 /* Set ASCII mode if -B flag was used */
663 if(data->state.prefer_ascii)
664 mode = "netascii";
665
666 switch(event) {
667
668 case TFTP_EVENT_INIT: /* Send the first packet out */
669 case TFTP_EVENT_TIMEOUT: /* Resend the first packet out */
670 /* Increment the retry counter, quit if over the limit */
671 state->retries++;
672 if(state->retries > state->retry_max) {
673 state->error = TFTP_ERR_NORESPONSE;
674 state->state = TFTP_STATE_FIN;
675 return result;
676 }
677
678 if(data->state.upload) {
679 /* If we are uploading, send a WRQ */
680 setpacketevent(&state->spacket, TFTP_EVENT_WRQ);
681 if(data->state.infilesize != -1)
682 Curl_pgrsSetUploadSize(data, data->state.infilesize);
683 }
684 else {
685 /* If we are downloading, send an RRQ */
686 setpacketevent(&state->spacket, TFTP_EVENT_RRQ);
C5: Client sends RRQ for download when not uploading
687 }
688 /* As RFC3617 describes the separator slash is not actually part of the
689 filename so we skip the always-present first letter of the path
690 string. */
691 if(!state->data->state.up.path[1]) {
692 failf(data, "Missing filename");
693 return CURLE_TFTP_ILLEGAL;
694 }
695 result = Curl_urldecode(&state->data->state.up.path[1], 0,
696 &filename, NULL, REJECT_ZERO);
697 if(result)
698 return result;
699
700 if(strlen(filename) + strlen(mode) + 4 > state->blksize) {
701 failf(data, "TFTP filename too long");
702 curlx_free(filename);
703 return CURLE_TFTP_ILLEGAL; /* too long filename field */
704 }
705
706 sbytes = 2 +
707 curl_msnprintf((char *)state->spacket.data + 2,
708 state->blksize,
709 "%s%c%s%c", filename, '\0', mode, '\0');
710 curlx_free(filename);
711
712 /* optional addition of TFTP options */
713 if(!data->set.tftp_no_options) {
714 char buf[64];
715 /* add tsize option */
716 curl_msnprintf(buf, sizeof(buf), "%" FMT_OFF_T,
717 data->state.upload && (data->state.infilesize != -1) ?
718 data->state.infilesize : 0);
719
720 result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_TSIZE);
721 if(result == CURLE_OK)
722 result = tftp_option_add(state, &sbytes, sbytes, buf);
723
724 /* add blksize option */
725 curl_msnprintf(buf, sizeof(buf), "%u", state->requested_blksize);
726 if(result == CURLE_OK)
727 result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_BLKSIZE);
728 if(result == CURLE_OK)
729 result = tftp_option_add(state, &sbytes, sbytes, buf);
730
731 /* add timeout option */
732 curl_msnprintf(buf, sizeof(buf), "%d", state->retry_time);
733 if(result == CURLE_OK)
734 result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_INTERVAL);
735 if(result == CURLE_OK)
736 result = tftp_option_add(state, &sbytes, sbytes, buf);
737
738 if(result != CURLE_OK) {
739 failf(data, "TFTP buffer too small for options");
740 return CURLE_TFTP_ILLEGAL;
741 }
742 }
743
744 /* the typecast for the 3rd argument is mostly for systems that do
745 not have a size_t argument, like older unixes that want an 'int' */
746#ifdef __AMIGA__
747#define CURL_SENDTO_ARG5(x) CURL_UNCONST(x)
748#else
749#define CURL_SENDTO_ARG5(x) (x)
750#endif
751 remote_addr = Curl_conn_get_remote_addr(data, FIRSTSOCKET);
752 if(!remote_addr)
753 return CURLE_FAILED_INIT;
754
755 senddata = sendto(state->sockfd, (void *)state->spacket.data,
756 (SEND_TYPE_ARG3)sbytes, 0,
757 CURL_SENDTO_ARG5(&remote_addr->curl_sa_addr),
758 (curl_socklen_t)remote_addr->addrlen);
759 if(senddata != (ssize_t)sbytes) {
760 char buffer[STRERROR_LEN];
761 failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
762 return CURLE_SEND_ERROR;
763 }
764 break;
765
766 case TFTP_EVENT_OACK:
C1: case TFTP_EVENT_OACK correctly checks data->state.upload before choosing direction
767 if(data->state.upload) {
768 result = tftp_connect_for_tx(state, event);
769 }
770 else {
771 result = tftp_connect_for_rx(state, event);
772 }
773 break;
774
775 case TFTP_EVENT_ACK: /* Connected for transmit */
C1: case TFTP_EVENT_ACK calls tftp_connect_for_tx without checking data->state.upload
C2: TFTP_EVENT_ACK case at line 775
C3: TFTP_EVENT_ACK triggers tftp_connect_for_tx unconditionally
C5: ACK reply forces transmit path
776 result = tftp_connect_for_tx(state, event);
777 break;
778
779 case TFTP_EVENT_DATA: /* Connected for receive */
C1: case TFTP_EVENT_DATA calls tftp_connect_for_rx without checking data->state.upload
C2: TFTP_EVENT_DATA case at line 779
780 result = tftp_connect_for_rx(state, event);
781 break;
782
783 case TFTP_EVENT_ERROR:
784 state->state = TFTP_STATE_FIN;
785 break;
786
787 default:
788 failf(state->data, "tftp_send_first: internal error");
789 return CURLE_TFTP_ILLEGAL;
790 }
791
792 return result;
793}
794
795/**********************************************************
796 *
797 * tftp_translate_code
798 *
799 * Translate internal error codes to CURL error codes
800 *
801 **********************************************************/
802static CURLcode tftp_translate_code(tftp_error_t error)
803{
804 CURLcode result = CURLE_OK;
805
806 if(error != TFTP_ERR_NONE) {
lib/sendf.clines 1127–1204 · lines 1152-1179 cited by the report
1127
1128 DEBUGASSERT(r);
1129 DEBUGASSERT(r->crt);
1130 DEBUGASSERT(r->phase == CURL_CR_CLIENT);
1131 DEBUGASSERT(!data->req.reader.stack);
1132
1133 data->req.reader.stack = r;
1134 clen = r->crt->total_length(data, r);
1135 /* if we do not have 0 length init, and CRLF conversion is wanted,
1136 * add the reader for it */
1137 if(clen &&
1138#ifdef CURL_PREFER_LF_LINEENDS
1139 (data->set.crlf || data->state.prefer_ascii)
1140#else
1141 data->set.crlf
1142#endif
1143 ) {
1144 result = cr_lc_add(data);
1145 if(result)
1146 return result;
1147 }
1148
1149 return result;
1150}
1151
1152CURLcode Curl_creader_set_fread(struct Curl_easy *data, curl_off_t len)
C4: Curl_creader_set_fread uses data->state.infilesize and the fread function/pointer from CURLOPT_READFUNCTION/CURLOPT_READDATA
1153{
1154 CURLcode result;
1155 struct Curl_creader *r;
1156 struct cr_in_ctx *ctx;
1157
1158 result = Curl_creader_create(&r, data, &cr_in, CURL_CR_CLIENT);
1159 if(result || !r)
1160 goto out;
1161 ctx = r->ctx;
1162 ctx->total_len = len;
1163
1164 cl_reset_reader(data);
1165 result = do_init_reader_stack(data, r);
1166out:
1167 CURL_TRC_READ(data, "add fread reader, len=%" FMT_OFF_T " -> %d",
1168 len, (int)result);
1169 return result;
1170}
1171
1172CURLcode Curl_creader_add(struct Curl_easy *data,
1173 struct Curl_creader *reader)
1174{
1175 CURLcode result;
1176 struct Curl_creader **anchor = &data->req.reader.stack;
1177
1178 if(!*anchor) {
1179 result = Curl_creader_set_fread(data, data->state.infilesize);
1180 if(result)
1181 return result;
1182 }
1183
1184 /* Insert the writer as first in its phase.
1185 * Skip existing readers of lower phases. */
1186 while(*anchor && (*anchor)->phase < reader->phase)
1187 anchor = &(*anchor)->next;
1188 reader->next = *anchor;
1189 *anchor = reader;
1190 return CURLE_OK;
1191}
1192
1193CURLcode Curl_creader_set(struct Curl_easy *data, struct Curl_creader *r)
1194{
1195 CURLcode result;
1196
1197 DEBUGASSERT(r);
1198 DEBUGASSERT(r->crt);
1199 DEBUGASSERT(r->phase == CURL_CR_CLIENT);
1200
1201 cl_reset_reader(data);
1202 result = do_init_reader_stack(data, r);
1203 if(result)
1204 Curl_creader_free(data, r);
lib/sendf.clines 1208–1247 · definition of Curl_client_read
1208CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen,
1209 size_t *nread, bool *eos)
1210{
1211 CURLcode result;
1212
1213 DEBUGASSERT(buf);
1214 DEBUGASSERT(blen);
1215 DEBUGASSERT(nread);
1216 DEBUGASSERT(eos);
1217 *nread = 0;
1218
1219 if(!data->req.reader.stack) {
1220 result = Curl_creader_set_fread(data, data->state.infilesize);
C4: Curl_client_read uses data->state.infilesize as length limit
1221 if(result)
1222 return result;
1223 DEBUGASSERT(data->req.reader.stack);
1224 }
1225 if(!data->req.reader_started) {
1226 Curl_rlimit_start(&data->progress.ul.rlimit, Curl_pgrs_now(data), -1);
1227 data->req.reader_started = TRUE;
1228 }
1229
1230 if(Curl_rlimit_active(&data->progress.ul.rlimit)) {
1231 curl_off_t ul_avail = Curl_rlimit_avail(&data->progress.ul.rlimit, NULL);
1232 if(ul_avail <= 0) {
1233 result = CURLE_OK;
1234 *eos = FALSE;
1235 goto out;
1236 }
1237 if(ul_avail < (curl_off_t)blen)
1238 blen = (size_t)ul_avail;
1239 }
1240 result = Curl_creader_read(data, data->req.reader.stack, buf, blen,
1241 nread, eos);
1242
1243out:
1244 CURL_TRC_READ(data, "client_read(len=%zu) -> %d, nread=%zu, eos=%d",
1245 blen, (int)result, *nread, *eos);
1246 return result;
1247}
lib/tftp.clines 358–499 · definition of tftp_tx
358static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event)
359{
360 struct Curl_easy *data = state->data;
361 ssize_t sbytes;
362 CURLcode result = CURLE_OK;
363 struct SingleRequest *k = &data->req;
364 size_t cb; /* Bytes currently read */
365 char buffer[STRERROR_LEN];
366 char *bufptr;
367 bool eos;
368
369 switch(event) {
370
371 case TFTP_EVENT_ACK:
372 case TFTP_EVENT_OACK:
373 if(event == TFTP_EVENT_ACK) {
C3: tftp_tx handles TFTP_EVENT_ACK and TFTP_EVENT_OACK, reading data via Curl_client_read
374 /* Ack the packet */
375 int rblock = getrpacketblock(&state->rpacket);
376
377 if(rblock != state->block &&
378 /* There is a bug in tftpd-hpa that causes it to send us an ACK for
379 * 65535 when the block number wraps to 0. To handle it, when we are
380 * expecting 0, also accept 65535. See
381 * https://www.syslinux.org/archives/2010-September/015612.html
382 * */
383 !(state->block == 0 && rblock == 65535)) {
384 /* This is not the expected block. Log it and up the retry counter */
385 infof(data, "Received ACK for block %d, expecting %d",
386 rblock, state->block);
387 state->retries++;
388 /* Bail out if over the maximum */
389 if(state->retries > state->retry_max) {
390 failf(data, "tftp_tx: giving up waiting for block %d ack",
391 state->block);
392 result = CURLE_SEND_ERROR;
393 }
394 else {
395 /* Re-send the data packet */
396 sbytes = sendto(state->sockfd, (void *)state->spacket.data,
397 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG,
398 (struct sockaddr *)&state->remote_addr,
399 state->remote_addrlen);
400 /* Check all sbytes were sent */
401 if(sbytes < 0) {
402 failf(data, "%s",
403 curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
404 result = CURLE_SEND_ERROR;
405 }
406 }
407
408 return result;
409 }
410 /* This is the expected packet. Reset the counters and send the next
411 block */
412 state->rx_time = time(NULL);
413 state->block++;
414 }
415 else
416 state->block = 1; /* first data block is 1 when using OACK */
417
418 state->retries = 0;
419 setpacketevent(&state->spacket, TFTP_EVENT_DATA);
420 setpacketblock(&state->spacket, state->block);
421 if(state->block > 1 && state->sbytes < state->blksize) {
422 state->state = TFTP_STATE_FIN;
423 return CURLE_OK;
424 }
425
426 /* TFTP considers data block size < 512 bytes as an end of session, so
427 * in some cases we must wait for additional data to build full (512 bytes)
428 * data block.
429 * */
430 state->sbytes = 0;
431 bufptr = (char *)state->spacket.data + 4;
432 do {
433 result = Curl_client_read(data, bufptr, state->blksize - state->sbytes,
C4: tftp_tx calls Curl_client_read to read upload data
434 &cb, &eos);
435 if(result)
436 return result;
437 state->sbytes += cb;
438 bufptr += cb;
439 } while(state->sbytes < state->blksize && cb);
440
441 sbytes = sendto(state->sockfd, (void *)state->spacket.data,
442 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG,
443 (struct sockaddr *)&state->remote_addr,
444 state->remote_addrlen);
445 /* Check all sbytes were sent */
446 if(sbytes < 0) {
447 failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
448 return CURLE_SEND_ERROR;
449 }
450 /* Update the progress meter */
451 k->writebytecount += state->sbytes;
452 Curl_pgrs_upload_inc(data, state->sbytes);
453 break;
454
455 case TFTP_EVENT_TIMEOUT:
456 /* Increment the retry counter and log the timeout */
457 state->retries++;
458 infof(data, "Timeout waiting for block %d ACK. "
459 "Retries = %d", NEXT_BLOCKNUM(state->block), state->retries);
460 /* Decide if we have had enough */
461 if(state->retries > state->retry_max) {
462 state->error = TFTP_ERR_TIMEOUT;
463 state->state = TFTP_STATE_FIN;
464 }
465 else {
466 /* Re-send the data packet */
467 sbytes = sendto(state->sockfd, (void *)state->spacket.data,
468 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG,
469 (struct sockaddr *)&state->remote_addr,
470 state->remote_addrlen);
471 /* Check all sbytes were sent */
472 if(sbytes < 0) {
473 failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
474 return CURLE_SEND_ERROR;
475 }
476 /* since this was a re-send, we remain at the still byte position */
477 Curl_pgrsSetUploadCounter(data, k->writebytecount);
478 }
479 break;
480
481 case TFTP_EVENT_ERROR:
482 state->state = TFTP_STATE_FIN;
483 setpacketevent(&state->spacket, TFTP_EVENT_ERROR);
484 setpacketblock(&state->spacket, state->block);
485 (void)sendto(state->sockfd, (void *)state->spacket.data, 4, SEND_4TH_ARG,
486 (struct sockaddr *)&state->remote_addr,
487 state->remote_addrlen);
488 /* do not bother with the return code, but if the socket is still up we
489 * should be a good TFTP client and let the server know we are done */
490 state->state = TFTP_STATE_FIN;
491 break;
492
493 default:
494 failf(data, "tftp_tx: internal error, event: %d", (int)event);
495 break;
496 }
497
498 return result;
499}
lib/tftp.clines 1036–1125 · lines 1079-1091 cited by the report; definition of tftp_receive_packet
Show 90 lines (not cited by the judge)
1036static CURLcode tftp_receive_packet(struct Curl_easy *data,
1037 struct tftp_conn *state)
1038{
1039 CURLcode result = CURLE_OK;
1040 struct Curl_sockaddr_storage remote_addr;
1041 curl_socklen_t fromlen = sizeof(remote_addr);
1042
1043 /* Receive the packet */
1044 state->rbytes = (int)recvfrom(state->sockfd,
1045 (void *)state->rpacket.data,
1046 (RECV_TYPE_ARG3)state->blksize + 4,
1047 0,
1048 (struct sockaddr *)&remote_addr,
1049 &fromlen);
1050 if((state->rbytes >= 0) && fromlen) {
1051 if(state->remote_pinned) {
1052 /* pinned, verify that it comes from the same address */
1053 if((state->remote_addrlen != fromlen) ||
1054 memcmp(&remote_addr, &state->remote_addr, fromlen)) {
1055 failf(data, "Data received from another address");
1056 return CURLE_RECV_ERROR;
1057 }
1058 }
1059 else {
1060 /* pin address on first use */
1061 state->remote_pinned = TRUE;
1062 state->remote_addrlen = fromlen;
1063 memcpy(&state->remote_addr, &remote_addr, fromlen);
1064 }
1065 }
1066
1067 /* Sanity check packet length */
1068 if(state->rbytes < 4) {
1069 failf(data, "Received too short packet");
1070 /* Not a timeout, but how best to handle it? */
1071 state->event = TFTP_EVENT_TIMEOUT;
1072 }
1073 else {
1074 /* The event is given by the TFTP packet time */
1075 unsigned short event = getrpacketevent(&state->rpacket);
1076 state->event = (tftp_event_t)event;
1077
1078 switch(state->event) {
1079 case TFTP_EVENT_DATA:
1080 /* Do not pass to the client empty or retransmitted packets */
1081 if(state->rbytes > 4 &&
1082 (NEXT_BLOCKNUM(state->block) == getrpacketblock(&state->rpacket))) {
1083 result = Curl_client_write(data, CLIENTWRITE_BODY,
1084 (const char *)state->rpacket.data + 4,
1085 state->rbytes - 4);
1086 if(result) {
1087 tftp_state_machine(state, TFTP_EVENT_ERROR);
1088 return result;
1089 }
1090 }
1091 break;
1092 case TFTP_EVENT_ERROR: {
1093 unsigned short error = getrpacketblock(&state->rpacket);
1094 const char *str = (const char *)state->rpacket.data + 4;
1095 size_t strn = state->rbytes - 4;
1096 state->error = (tftp_error_t)error;
1097 if(tftp_strnlen(str, strn) < strn)
1098 infof(data, "TFTP error: %s", str);
1099 break;
1100 }
1101 case TFTP_EVENT_ACK:
1102 break;
1103 case TFTP_EVENT_OACK:
1104 result = tftp_parse_option_ack(state,
1105 (const char *)state->rpacket.data + 2,
1106 state->rbytes-2);
1107 if(result)
1108 return result;
1109 break;
1110 case TFTP_EVENT_RRQ:
1111 case TFTP_EVENT_WRQ:
1112 default:
1113 failf(data, "%s", "Internal error: Unexpected packet");
1114 break;
1115 }
1116
1117 /* Update the progress meter */
1118 result = Curl_pgrsUpdate(data);
1119 if(result) {
1120 tftp_state_machine(state, TFTP_EVENT_ERROR);
1121 return result;
1122 }
1123 }
1124 return result;
1125}
lib/tftp.clines 1178–1232 · lines 1203-1207 cited by the report
Show 55 lines (not cited by the judge)
1178 timeout_ms = tftp_state_timeout(state, &event);
1179 if(timeout_ms < 0) {
1180 failf(data, "TFTP response timeout");
1181 return CURLE_OPERATION_TIMEDOUT;
1182 }
1183 if(event != TFTP_EVENT_NONE) {
1184 result = tftp_state_machine(state, event);
1185 if(result)
1186 return result;
1187 *done = (state->state == TFTP_STATE_FIN);
1188 if(*done)
1189 /* Tell curl we are done */
1190 Curl_xfer_setup_nop(data);
1191 }
1192 else {
1193 /* no timeouts to handle, check our socket */
1194 int rc = SOCKET_READABLE(state->sockfd, 0);
1195
1196 if(rc == -1) {
1197 /* bail out */
1198 int sockerr = SOCKERRNO;
1199 char buffer[STRERROR_LEN];
1200 failf(data, "%s", curlx_strerror(sockerr, buffer, sizeof(buffer)));
1201 state->event = TFTP_EVENT_ERROR;
1202 }
1203 else if(rc) {
1204 result = tftp_receive_packet(data, state);
1205 if(result)
1206 return result;
1207 result = tftp_state_machine(state, state->event);
1208 if(result)
1209 return result;
1210 *done = (state->state == TFTP_STATE_FIN);
1211 if(*done)
1212 /* Tell curl we are done */
1213 Curl_xfer_setup_nop(data);
1214 }
1215 /* if rc == 0, then select() timed out */
1216 }
1217
1218 return result;
1219}
1220
1221/**********************************************************
1222 *
1223 * tftp_doing
1224 *
1225 * Called from multi.c while DOing
1226 *
1227 **********************************************************/
1228static CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done)
1229{
1230 CURLcode result;
1231 result = tftp_multi_statemach(data, dophase_done);
1232
lib/protocol.hlines 71–72 · definition of CURLPROTO_REDIR
Show 2 lines (not cited by the judge)
71#define CURLPROTO_REDIR (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | \
72 CURLPROTO_FTPS)
lib/sendf.clines 373–399 · definition of Curl_client_write
Show 27 lines (not cited by the judge)
373CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *buf,
374 size_t len)
375{
376 CURLcode result;
377
378 /* it is one of those, at least */
379 DEBUGASSERT(type &
380 (CLIENTWRITE_BODY | CLIENTWRITE_HEADER | CLIENTWRITE_INFO));
381 /* BODY is only BODY (with optional EOS) */
382 DEBUGASSERT(!(type & CLIENTWRITE_BODY) ||
383 ((type & ~(CLIENTWRITE_BODY | CLIENTWRITE_EOS)) == 0));
384 /* INFO is only INFO (with optional EOS) */
385 DEBUGASSERT(!(type & CLIENTWRITE_INFO) ||
386 ((type & ~(CLIENTWRITE_INFO | CLIENTWRITE_EOS)) == 0));
387
388 if(!data->req.writer.stack) {
389 result = do_init_writer_stack(data);
390 if(result)
391 return result;
392 DEBUGASSERT(data->req.writer.stack);
393 }
394
395 result = Curl_cwriter_write(data, data->req.writer.stack, type, buf, len);
396 CURL_TRC_WRITE(data, "client_write(type=%x, len=%zu) -> %d",
397 (unsigned int)type, len, (int)result);
398 return result;
399}
lib/setopt.clines 2936–2955 · definition of curl_easy_setopt
Show 20 lines (not cited by the judge)
2936CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...)
2937{
2938 struct Curl_eapi_guard guard;
2939 CURLcode result;
2940
2941 if(CURL_EAPI_ENTER(&guard, curl, easy_setopt, &result)) {
2942 struct Curl_easy *data = curl;
2943 va_list arg;
2944
2945 va_start(arg, option);
2946
2947 result = Curl_vsetopt(data, option, arg);
2948
2949 va_end(arg);
2950 if(result == CURLE_BAD_FUNCTION_ARGUMENT)
2951 failf(data, "setopt 0x%x got bad argument", (unsigned int)option);
2952 }
2953 CURL_EAPI_LEAVE(&guard);
2954 return result;
2955}
src/curlinfo.clines 273–281 · definition of main
Show 9 lines (not cited by the judge)
273int main(void)
274{
275 size_t i;
276
277 for(i = 0; i < CURL_ARRAYSIZE(disabled); i++)
278 puts(disabled[i]);
279
280 return 0;
281}
lib/tftp.clines 637–649 · definition of tftp_connect_for_rx
Show 13 lines (not cited by the judge)
637static CURLcode tftp_connect_for_rx(struct tftp_conn *state,
638 tftp_event_t event)
639{
640 CURLcode result;
641
642 infof(state->data, "%s", "Connected for receive");
643
644 state->state = TFTP_STATE_RX;
645 result = tftp_set_timeouts(state);
646 if(result)
647 return result;
648 return tftp_rx(state, event);
649}
lib/tftp.clines 501–513 · definition of tftp_connect_for_tx
Show 13 lines (not cited by the judge)
501static CURLcode tftp_connect_for_tx(struct tftp_conn *state,
502 tftp_event_t event)
503{
504 CURLcode result;
505
506 infof(state->data, "%s", "Connected for transmit");
507
508 state->state = TFTP_STATE_TX;
509 result = tftp_set_timeouts(state);
510 if(result)
511 return result;
512 return tftp_tx(state, event);
513}