]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
telnet: use dynbuf instad of malloc for escape buffer
authorDaniel Stenberg <daniel@haxx.se>
Sun, 7 Jan 2024 15:28:15 +0000 (16:28 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 9 Jan 2024 08:21:01 +0000 (09:21 +0100)
Previously, send_telnet_data() would malloc + free a buffer every time
for escaping IAC codes. Now, it reuses a dynbuf for this purpose.

Closes #12652

lib/telnet.c

index 67667e347e581d61177e4acb05b9418a9f6857a6..b626bc56e53c91ad88d9765976dbe203ee694375 100644 (file)
@@ -160,6 +160,7 @@ struct TELNET {
   unsigned short subopt_wsy;         /* Set with suboption NAWS */
   TelnetReceive telrcv_state;
   struct curl_slist *telnet_vars;    /* Environment variables */
+  struct dynbuf out;                 /* output buffer */
 
   /* suboptions */
   unsigned char subbuffer[SUBBUFSIZE];
@@ -204,6 +205,7 @@ CURLcode init_telnet(struct Curl_easy *data)
   if(!tn)
     return CURLE_OUT_OF_MEMORY;
 
+  Curl_dyn_init(&tn->out, 0xffff);
   data->req.p.telnet = tn; /* make us known */
 
   tn->telrcv_state = CURL_TS_DATA;
@@ -1229,37 +1231,33 @@ process_iac:
 static CURLcode send_telnet_data(struct Curl_easy *data,
                                  char *buffer, ssize_t nread)
 {
-  ssize_t escapes, i, outlen;
-  unsigned char *outbuf = NULL;
+  ssize_t i, outlen;
+  unsigned char *outbuf;
   CURLcode result = CURLE_OK;
-  ssize_t bytes_written, total_written;
+  ssize_t bytes_written, total_written = 0;
   struct connectdata *conn = data->conn;
+  struct TELNET *tn = data->req.p.telnet;
 
-  /* Determine size of new buffer after escaping */
-  escapes = 0;
-  for(i = 0; i < nread; i++)
-    if((unsigned char)buffer[i] == CURL_IAC)
-      escapes++;
-  outlen = nread + escapes;
+  DEBUGASSERT(tn);
 
-  if(outlen == nread)
-    outbuf = (unsigned char *)buffer;
-  else {
-    ssize_t j;
-    outbuf = malloc(nread + escapes + 1);
-    if(!outbuf)
-      return CURLE_OUT_OF_MEMORY;
+  if(memchr(buffer, CURL_IAC, nread)) {
+    /* only use the escape buffer when necessary */
+    Curl_dyn_reset(&tn->out);
 
-    j = 0;
-    for(i = 0; i < nread; i++) {
-      outbuf[j++] = (unsigned char)buffer[i];
-      if((unsigned char)buffer[i] == CURL_IAC)
-        outbuf[j++] = CURL_IAC;
+    for(i = 0; i < nread && !result; i++) {
+      result = Curl_dyn_addn(&tn->out, &buffer[i], 1);
+      if(!result && ((unsigned char)buffer[i] == CURL_IAC))
+        /* IAC is FF in hex */
+        result = Curl_dyn_addn(&tn->out, "\xff", 1);
     }
-    outbuf[j] = '\0';
-  }
 
-  total_written = 0;
+    outlen = Curl_dyn_len(&tn->out);
+    outbuf = Curl_dyn_uptr(&tn->out);
+  }
+  else {
+    outlen = nread;
+    outbuf = (unsigned char *)buffer;
+  }
   while(!result && total_written < outlen) {
     /* Make sure socket is writable to avoid EWOULDBLOCK condition */
     struct pollfd pfd[1];
@@ -1272,19 +1270,13 @@ static CURLcode send_telnet_data(struct Curl_easy *data,
         break;
       default:                    /* write! */
         bytes_written = 0;
-        result = Curl_nwrite(data, FIRSTSOCKET,
-                             outbuf + total_written,
-                             outlen - total_written,
-                             &bytes_written);
+        result = Curl_nwrite(data, FIRSTSOCKET, outbuf + total_written,
+                             outlen - total_written, &bytes_written);
         total_written += bytes_written;
         break;
     }
   }
 
-  /* Free malloc copy if escaped */
-  if(outbuf != (unsigned char *)buffer)
-    free(outbuf);
-
   return result;
 }
 
@@ -1300,6 +1292,7 @@ static CURLcode telnet_done(struct Curl_easy *data,
 
   curl_slist_free_all(tn->telnet_vars);
   tn->telnet_vars = NULL;
+  Curl_dyn_free(&tn->out);
   return CURLE_OK;
 }