Coverage Report

Created: 2026-09-14 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/dict.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
#include "curl_setup.h"
25
#include "urldata.h"
26
#include "dict.h"
27
28
#ifndef CURL_DISABLE_DICT
29
30
#ifdef HAVE_NETINET_IN_H
31
#include <netinet/in.h>
32
#endif
33
#ifdef HAVE_NETDB_H
34
#include <netdb.h>
35
#endif
36
#ifdef HAVE_ARPA_INET_H
37
#include <arpa/inet.h>
38
#endif
39
#ifdef HAVE_NET_IF_H
40
#include <net/if.h>
41
#endif
42
#ifdef HAVE_SYS_IOCTL_H
43
#include <sys/ioctl.h>
44
#endif
45
46
#ifdef HAVE_SYS_PARAM_H
47
#include <sys/param.h>
48
#endif
49
50
#ifdef HAVE_SYS_SELECT_H
51
#include <sys/select.h>
52
#elif defined(HAVE_UNISTD_H)
53
#include <unistd.h>
54
#endif
55
56
#include "transfer.h"
57
#include "curl_trc.h"
58
#include "connect.h"
59
#include "select.h"
60
#include "escape.h"
61
62
1.86k
#define DICT_MATCH   "/MATCH:"
63
1.86k
#define DICT_MATCH2  "/M:"
64
1.78k
#define DICT_MATCH3  "/FIND:"
65
1.78k
#define DICT_DEFINE  "/DEFINE:"
66
1.78k
#define DICT_DEFINE2 "/D:"
67
1.71k
#define DICT_DEFINE3 "/LOOKUP:"
68
69
153
#define DYN_DICT_WORD 10000
70
static char *unescape_word(const char *input)
71
153
{
72
153
  struct dynbuf out;
73
153
  const char *ptr;
74
153
  CURLcode result = CURLE_OK;
75
153
  curlx_dyn_init(&out, DYN_DICT_WORD);
76
77
  /* According to RFC2229 section 2.2, these letters need to be escaped with
78
     \[letter] */
79
129k
  for(ptr = input; *ptr; ptr++) {
80
129k
    char ch = *ptr;
81
129k
    if((ch <= 32) || (ch == 127) ||
82
60.7k
       (ch == '\'') || (ch == '\"') || (ch == '\\'))
83
73.9k
      result = curlx_dyn_addn(&out, "\\", 1);
84
129k
    if(!result)
85
129k
      result = curlx_dyn_addn(&out, ptr, 1);
86
129k
    if(result)
87
12
      return NULL;
88
129k
  }
89
141
  return curlx_dyn_ptr(&out);
90
153
}
91
92
/* sendf() sends formatted data to the server */
93
static CURLcode sendf(struct Curl_easy *data,
94
                      const char *fmt, ...) CURL_PRINTF(2, 3);
95
96
static CURLcode sendf(struct Curl_easy *data, const char *fmt, ...)
97
1.85k
{
98
1.85k
  curl_socket_t sockfd = data->conn->sock[FIRSTSOCKET];
99
1.85k
  size_t bytes_written;
100
1.85k
  size_t write_len;
101
1.85k
  CURLcode result = CURLE_OK;
102
1.85k
  timediff_t timeout_ms;
103
1.85k
  int what;
104
1.85k
  char *s;
105
1.85k
  char *sptr;
106
1.85k
  va_list ap;
107
1.85k
  va_start(ap, fmt);
108
1.85k
  s = curl_mvaprintf(fmt, ap); /* returns an allocated string */
109
1.85k
  va_end(ap);
110
1.85k
  if(!s)
111
0
    return CURLE_OUT_OF_MEMORY; /* failure */
112
113
1.85k
  bytes_written = 0;
114
1.85k
  write_len = strlen(s);
115
1.85k
  sptr = s;
116
117
1.85k
  for(;;) {
118
    /* Write the buffer to the socket */
119
1.85k
    result = Curl_xfer_send(data, sptr, write_len, FALSE, &bytes_written);
120
121
1.85k
    if(result)
122
0
      break;
123
124
1.85k
    Curl_debug(data, CURLINFO_DATA_OUT, sptr, bytes_written);
125
126
1.85k
    if(bytes_written != write_len) {
127
      /* if not all was written at once, we must advance the pointer, decrease
128
         the size left and try again! */
129
31
      write_len -= bytes_written;
130
31
      sptr += bytes_written;
131
31
    }
132
1.82k
    else
133
1.82k
      break;
134
135
31
    timeout_ms = Curl_timeleft_ms(data);
136
31
    if(timeout_ms < 0) {
137
0
      result = CURLE_OPERATION_TIMEDOUT;
138
0
      break;
139
0
    }
140
31
    if(!timeout_ms)
141
7
      timeout_ms = TIMEDIFF_T_MAX;
142
143
    /* Do not busyloop. The entire loop thing is a workaround as it causes a
144
       BLOCKING behavior which is a NO-NO. This function should rather be
145
       split up in a do and a doing piece where the pieces that are not
146
       possible to send now will be sent in the doing function repeatedly
147
       until the entire request is sent. */
148
31
    what = SOCKET_WRITABLE(sockfd, timeout_ms);
149
31
    if(what < 0) {
150
0
      result = CURLE_SEND_ERROR;
151
0
      break;
152
0
    }
153
31
    else if(!what) {
154
31
      result = CURLE_OPERATION_TIMEDOUT;
155
31
      break;
156
31
    }
157
31
  }
158
159
1.85k
  curlx_free(s); /* free the output string */
160
161
1.85k
  return result;
162
1.85k
}
163
164
static CURLcode dict_do(struct Curl_easy *data, bool *done)
165
1.87k
{
166
1.87k
  char *word;
167
1.87k
  char *eword = NULL;
168
1.87k
  char *ppath;
169
1.87k
  char *database = NULL;
170
1.87k
  char *strategy = NULL;
171
1.87k
  char *nthdef = NULL; /* This is not part of the protocol, but required
172
                          by RFC 2229 */
173
1.87k
  CURLcode result;
174
175
1.87k
  char *path;
176
177
1.87k
  *done = TRUE; /* unconditionally */
178
179
  /* URL-decode path before further evaluation */
180
1.87k
  result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL);
181
1.87k
  if(result)
182
11
    return result;
183
184
1.86k
  if(curl_strnequal(path, DICT_MATCH, CURL_CSTRLEN(DICT_MATCH)) ||
185
1.86k
     curl_strnequal(path, DICT_MATCH2, CURL_CSTRLEN(DICT_MATCH2)) ||
186
1.78k
     curl_strnequal(path, DICT_MATCH3, CURL_CSTRLEN(DICT_MATCH3))) {
187
188
80
    word = strchr(path, ':');
189
80
    if(word) {
190
80
      word++;
191
80
      database = strchr(word, ':');
192
80
      if(database) {
193
29
        *database++ = (char)0;
194
29
        strategy = strchr(database, ':');
195
29
        if(strategy) {
196
16
          *strategy++ = (char)0;
197
16
          nthdef = strchr(strategy, ':');
198
16
          if(nthdef) {
199
7
            *nthdef = (char)0;
200
7
          }
201
16
        }
202
29
      }
203
80
    }
204
205
80
    if(!word || (*word == (char)0)) {
206
9
      infof(data, "lookup word is missing");
207
9
    }
208
80
    eword = unescape_word((!word || (*word == (char)0)) ? "default" : word);
209
80
    if(!eword) {
210
4
      result = CURLE_OUT_OF_MEMORY;
211
4
      goto error;
212
4
    }
213
214
76
    result = sendf(data,
215
76
                   "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
216
76
                   "MATCH "
217
76
                   "%s "    /* database */
218
76
                   "%s "    /* strategy */
219
76
                   "%s\r\n" /* word */
220
76
                   "QUIT\r\n",
221
76
                   (!database || (*database == (char)0)) ? "!" : database,
222
76
                   (!strategy || (*strategy == (char)0)) ? "." : strategy,
223
76
                   eword);
224
225
76
    if(result) {
226
2
      failf(data, "Failed sending DICT request");
227
2
      goto error;
228
2
    }
229
74
    Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
230
74
  }
231
1.78k
  else if(curl_strnequal(path, DICT_DEFINE, CURL_CSTRLEN(DICT_DEFINE)) ||
232
1.78k
          curl_strnequal(path, DICT_DEFINE2, CURL_CSTRLEN(DICT_DEFINE2)) ||
233
1.71k
          curl_strnequal(path, DICT_DEFINE3, CURL_CSTRLEN(DICT_DEFINE3))) {
234
235
73
    word = strchr(path, ':');
236
73
    if(word) {
237
73
      word++;
238
73
      database = strchr(word, ':');
239
73
      if(database) {
240
23
        *database++ = (char)0;
241
23
        nthdef = strchr(database, ':');
242
23
        if(nthdef) {
243
13
          *nthdef = (char)0;
244
13
        }
245
23
      }
246
73
    }
247
248
73
    if(!word || (*word == (char)0)) {
249
5
      infof(data, "lookup word is missing");
250
5
    }
251
73
    eword = unescape_word((!word || (*word == (char)0)) ? "default" : word);
252
73
    if(!eword) {
253
8
      result = CURLE_OUT_OF_MEMORY;
254
8
      goto error;
255
8
    }
256
257
65
    result = sendf(data,
258
65
                   "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
259
65
                   "DEFINE "
260
65
                   "%s "     /* database */
261
65
                   "%s\r\n"  /* word */
262
65
                   "QUIT\r\n",
263
65
                   (!database || (*database == (char)0)) ? "!" : database,
264
65
                   eword);
265
266
65
    if(result) {
267
6
      failf(data, "Failed sending DICT request");
268
6
      goto error;
269
6
    }
270
59
    Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
271
59
  }
272
1.71k
  else {
273
274
1.71k
    ppath = strchr(path, '/');
275
1.71k
    if(ppath) {
276
1.71k
      int i;
277
278
1.71k
      ppath++;
279
6.94M
      for(i = 0; ppath[i]; i++) {
280
6.94M
        if(ppath[i] == ':')
281
809
          ppath[i] = ' ';
282
6.94M
      }
283
1.71k
      result = sendf(data,
284
1.71k
                     "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
285
1.71k
                     "%s\r\n"
286
1.71k
                     "QUIT\r\n", ppath);
287
1.71k
      if(result) {
288
23
        failf(data, "Failed sending DICT request");
289
23
        goto error;
290
23
      }
291
292
1.69k
      Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
293
1.69k
    }
294
1.71k
  }
295
296
1.86k
error:
297
1.86k
  curlx_free(eword);
298
1.86k
  curlx_free(path);
299
1.86k
  return result;
300
1.86k
}
301
302
/*
303
 * DICT protocol
304
 */
305
const struct Curl_protocol Curl_protocol_dict = {
306
  ZERO_NULL,                            /* setup_connection */
307
  dict_do,                              /* do_it */
308
  ZERO_NULL,                            /* done */
309
  ZERO_NULL,                            /* do_more */
310
  ZERO_NULL,                            /* connect_it */
311
  ZERO_NULL,                            /* connecting */
312
  ZERO_NULL,                            /* doing */
313
  ZERO_NULL,                            /* proto_pollset */
314
  ZERO_NULL,                            /* doing_pollset */
315
  ZERO_NULL,                            /* domore_pollset */
316
  ZERO_NULL,                            /* perform_pollset */
317
  ZERO_NULL,                            /* disconnect */
318
  ZERO_NULL,                            /* write_resp */
319
  ZERO_NULL,                            /* write_resp_hd */
320
  ZERO_NULL,                            /* connection_is_dead */
321
  ZERO_NULL,                            /* attach connection */
322
  ZERO_NULL,                            /* follow */
323
};
324
325
#endif /* CURL_DISABLE_DICT */