Tor  0.4.7.0-alpha-dev
tor-resolve.c
1 /* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson
2  * Copyright (c) 2007-2021, The Tor Project, Inc.
3  */
4 /* See LICENSE for licensing information */
5 
6 #include "orconfig.h"
7 
8 #include "lib/arch/bytes.h"
9 #include "lib/log/log.h"
10 #include "lib/malloc/malloc.h"
11 #include "lib/net/address.h"
12 #include "lib/net/resolve.h"
13 #include "lib/net/socket.h"
14 #include "lib/sandbox/sandbox.h"
15 #include "lib/string/util_string.h"
16 
17 #include "lib/net/socks5_status.h"
18 #include "trunnel/socks5.h"
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stdarg.h>
23 #include <string.h>
24 
25 #ifdef HAVE_NETINET_IN_H
26 #include <netinet/in.h>
27 #endif
28 #ifdef HAVE_ARPA_INET_H
29 #include <arpa/inet.h>
30 #endif
31 #ifdef HAVE_SYS_SOCKET_H
32 #include <sys/socket.h>
33 #endif
34 #ifdef HAVE_SYS_TYPES_H
35 #include <sys/types.h> /* Must be included before sys/stat.h for Ultrix */
36 #endif
37 #ifdef HAVE_ERRNO_H
38 #include <errno.h>
39 #endif
40 
41 #ifdef _WIN32
42 #include <winsock2.h>
43 #include <ws2tcpip.h>
44 #endif
45 
46 #define RESPONSE_LEN_4 8
47 #define log_sock_error(act, _s) \
48  STMT_BEGIN \
49  log_fn(LOG_ERR, LD_NET, "Error while %s: %s", act, \
50  tor_socket_strerror(tor_socket_errno(_s))); \
51  STMT_END
52 
53 static void usage(void) ATTR_NORETURN;
54 
55 /**
56  * Set <b>out</b> to a pointer to newly allocated buffer containing
57  * SOCKS4a RESOLVE request with <b>username</b> and <b>hostname</b>.
58  * Return number of bytes in the buffer if succeeded or -1 if failed.
59  */
60 static ssize_t
61 build_socks4a_resolve_request(uint8_t **out,
62  const char *username,
63  const char *hostname)
64 {
65  tor_assert(out);
66  tor_assert(username);
67  tor_assert(hostname);
68 
69  const char *errmsg = NULL;
70  uint8_t *output = NULL;
71  socks4_client_request_t *rq = socks4_client_request_new();
72 
73  socks4_client_request_set_version(rq, 4);
74  socks4_client_request_set_command(rq, CMD_RESOLVE);
75  socks4_client_request_set_port(rq, 0);
76  socks4_client_request_set_addr(rq, 0x00000001u);
77  socks4_client_request_set_username(rq, username);
78  socks4_client_request_set_socks4a_addr_hostname(rq, hostname);
79 
80  errmsg = socks4_client_request_check(rq);
81  if (errmsg) {
82  goto cleanup;
83  }
84 
85  ssize_t encoded_len = socks4_client_request_encoded_len(rq);
86  if (encoded_len <= 0) {
87  errmsg = "socks4_client_request_encoded_len failed";
88  goto cleanup;
89  }
90 
91  output = tor_malloc(encoded_len);
92  memset(output, 0, encoded_len);
93 
94  encoded_len = socks4_client_request_encode(output, encoded_len, rq);
95  if (encoded_len <= 0) {
96  errmsg = "socks4_client_request_encode failed";
97  goto cleanup;
98  }
99 
100  *out = output;
101 
102  cleanup:
103  socks4_client_request_free(rq);
104  if (errmsg) {
105  log_err(LD_NET, "build_socks4a_resolve_request failed: %s", errmsg);
106  *out = NULL;
107  tor_free(output);
108  }
109  return errmsg ? -1 : encoded_len;
110 }
111 
112 #define SOCKS5_ATYPE_HOSTNAME 0x03
113 #define SOCKS5_ATYPE_IPV4 0x01
114 #define SOCKS5_ATYPE_IPV6 0x04
115 
116 /**
117  * Set <b>out</b> to pointer to newly allocated buffer containing
118  * SOCKS5 RESOLVE/RESOLVE_PTR request with given <b>hostname<b>.
119  * Generate a reverse request if <b>reverse</b> is true.
120  * Return the number of bytes in the buffer if succeeded or -1 if failed.
121  */
122 static ssize_t
123 build_socks5_resolve_request(uint8_t **out,
124  const char *hostname,
125  int reverse)
126 {
127  const char *errmsg = NULL;
128  uint8_t *outbuf = NULL;
129  int is_ip_address;
130  tor_addr_t addr;
131  size_t addrlen;
132  int ipv6;
133  is_ip_address = tor_addr_parse(&addr, hostname) != -1;
134  if (!is_ip_address && reverse) {
135  log_err(LD_GENERAL, "Tried to do a reverse lookup on a non-IP!");
136  return -1;
137  }
138  ipv6 = reverse && tor_addr_family(&addr) == AF_INET6;
139  addrlen = reverse ? (ipv6 ? 16 : 4) : 1 + strlen(hostname);
140  if (addrlen > UINT8_MAX) {
141  log_err(LD_GENERAL, "Hostname is too long!");
142  return -1;
143  }
144 
145  socks5_client_request_t *rq = socks5_client_request_new();
146 
147  socks5_client_request_set_version(rq, 5);
148  /* RESOLVE_PTR or RESOLVE */
149  socks5_client_request_set_command(rq, reverse ? CMD_RESOLVE_PTR :
150  CMD_RESOLVE);
151  socks5_client_request_set_reserved(rq, 0);
152 
153  uint8_t atype = SOCKS5_ATYPE_HOSTNAME;
154  if (reverse)
155  atype = ipv6 ? SOCKS5_ATYPE_IPV6 : SOCKS5_ATYPE_IPV4;
156 
157  socks5_client_request_set_atype(rq, atype);
158 
159  switch (atype) {
160  case SOCKS5_ATYPE_IPV4: {
161  socks5_client_request_set_dest_addr_ipv4(rq,
162  tor_addr_to_ipv4h(&addr));
163  } break;
164  case SOCKS5_ATYPE_IPV6: {
165  uint8_t *ipv6_array =
166  socks5_client_request_getarray_dest_addr_ipv6(rq);
167 
168  tor_assert(ipv6_array);
169 
170  memcpy(ipv6_array, tor_addr_to_in6_addr8(&addr), 16);
171  } break;
172 
173  case SOCKS5_ATYPE_HOSTNAME: {
174  domainname_t *dn = domainname_new();
175  domainname_set_len(dn, addrlen - 1);
176  domainname_setlen_name(dn, addrlen - 1);
177  char *dn_buf = domainname_getarray_name(dn);
178  memcpy(dn_buf, hostname, addrlen - 1);
179 
180  errmsg = domainname_check(dn);
181 
182  if (errmsg) {
183  domainname_free(dn);
184  goto cleanup;
185  } else {
186  socks5_client_request_set_dest_addr_domainname(rq, dn);
187  }
188  } break;
189  default:
190  tor_assert_unreached();
191  break;
192  }
193 
194  socks5_client_request_set_dest_port(rq, 0);
195 
196  errmsg = socks5_client_request_check(rq);
197  if (errmsg) {
198  goto cleanup;
199  }
200 
201  ssize_t encoded_len = socks5_client_request_encoded_len(rq);
202  if (encoded_len < 0) {
203  errmsg = "Cannot predict encoded length";
204  goto cleanup;
205  }
206 
207  outbuf = tor_malloc(encoded_len);
208  memset(outbuf, 0, encoded_len);
209 
210  encoded_len = socks5_client_request_encode(outbuf, encoded_len, rq);
211  if (encoded_len < 0) {
212  errmsg = "encoding failed";
213  goto cleanup;
214  }
215 
216  *out = outbuf;
217 
218  cleanup:
219  socks5_client_request_free(rq);
220  if (errmsg) {
221  tor_free(outbuf);
222  log_err(LD_NET, "build_socks5_resolve_request failed with error: %s",
223  errmsg);
224  }
225 
226  return errmsg ? -1 : encoded_len;
227 }
228 
229 /** Set *<b>out</b> to a newly allocated SOCKS4a resolve request with
230  * <b>username</b> and <b>hostname</b> as provided. Return the number
231  * of bytes in the request. */
232 static ssize_t
233 build_socks_resolve_request(uint8_t **out,
234  const char *username,
235  const char *hostname,
236  int reverse,
237  int version)
238 {
239  tor_assert(out);
240  tor_assert(username);
241  tor_assert(hostname);
242 
243  tor_assert(version == 4 || version == 5);
244 
245  if (version == 4) {
246  return build_socks4a_resolve_request(out, username, hostname);
247  } else if (version == 5) {
248  return build_socks5_resolve_request(out, hostname, reverse);
249  }
250 
251  tor_assert_unreached();
252  return -1;
253 }
254 
255 static void
256 onion_warning(const char *hostname)
257 {
258  log_warn(LD_NET,
259  "%s is a hidden service; those don't have IP addresses. "
260  "You can use the AutomapHostsOnResolve option to have Tor return a "
261  "fake address for hidden services. Or you can have your "
262  "application send the address to Tor directly; we recommend an "
263  "application that uses SOCKS 5 with hostnames.",
264  hostname);
265 }
266 
267 /** Given a <b>len</b>-byte SOCKS4a response in <b>response</b>, set
268  * *<b>addr_out</b> to the address it contains (in host order).
269  * Return 0 on success, -1 on error.
270  */
271 static int
272 parse_socks4a_resolve_response(const char *hostname,
273  const char *response, size_t len,
274  tor_addr_t *addr_out)
275 {
276  int result = 0;
277  uint8_t status;
278  tor_assert(response);
279  tor_assert(addr_out);
280 
281  socks4_server_reply_t *reply;
282 
283  ssize_t parsed = socks4_server_reply_parse(&reply,
284  (const uint8_t *)response,
285  len);
286 
287  if (parsed == -1) {
288  log_warn(LD_PROTOCOL, "Failed parsing SOCKS4a response");
289  result = -1; goto cleanup;
290  }
291 
292  if (parsed == -2) {
293  log_warn(LD_PROTOCOL,"Truncated socks response.");
294  result = -1; goto cleanup;
295  }
296 
297  if (socks4_server_reply_get_version(reply) != 0) { /* version: 0 */
298  log_warn(LD_PROTOCOL,"Nonzero version in socks response: bad format.");
299  result = -1; goto cleanup;
300  }
301  if (socks4_server_reply_get_port(reply) != 0) { /* port: 0 */
302  log_warn(LD_PROTOCOL,"Nonzero port in socks response: bad format.");
303  result = -1; goto cleanup;
304  }
305  status = socks4_server_reply_get_status(reply);
306  if (status != 90) {
307  log_warn(LD_NET,"Got status response '%d': socks request failed.", status);
308  if (!strcasecmpend(hostname, ".onion")) {
309  onion_warning(hostname);
310  result = -1; goto cleanup;
311  }
312  result = -1; goto cleanup;
313  }
314 
315  tor_addr_from_ipv4h(addr_out, socks4_server_reply_get_addr(reply));
316 
317  cleanup:
318  socks4_server_reply_free(reply);
319  return result;
320 }
321 
322 /* It would be nice to let someone know what SOCKS5 issue a user may have */
323 static const char *
324 socks5_reason_to_string(char reason)
325 {
326  switch (reason) {
327  case SOCKS5_SUCCEEDED:
328  return "succeeded";
329  case SOCKS5_GENERAL_ERROR:
330  return "general error";
331  case SOCKS5_NOT_ALLOWED:
332  return "not allowed";
333  case SOCKS5_NET_UNREACHABLE:
334  return "network is unreachable";
335  case SOCKS5_HOST_UNREACHABLE:
336  return "host is unreachable";
337  case SOCKS5_CONNECTION_REFUSED:
338  return "connection refused";
339  case SOCKS5_TTL_EXPIRED:
340  return "ttl expired";
341  case SOCKS5_COMMAND_NOT_SUPPORTED:
342  return "command not supported";
343  case SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED:
344  return "address type not supported";
345  default:
346  return "unknown SOCKS5 code";
347  }
348 }
349 
350 /** Send a resolve request for <b>hostname</b> to the Tor listening on
351  * <b>sockshost</b>:<b>socksport</b>. Store the resulting IPv4
352  * address (in host order) into *<b>result_addr</b>.
353  */
354 static int
355 do_resolve(const char *hostname,
356  const tor_addr_t *sockshost, uint16_t socksport,
357  int reverse, int version,
358  tor_addr_t *result_addr, char **result_hostname)
359 {
360  int s = -1;
361  struct sockaddr_storage ss;
362  socklen_t socklen;
363  uint8_t *req = NULL;
364  ssize_t len = 0;
365 
366  tor_assert(hostname);
367  tor_assert(result_addr);
368  tor_assert(version == 4 || version == 5);
369 
370  tor_addr_make_unspec(result_addr);
371  *result_hostname = NULL;
372 
373  s = tor_open_socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
374  if (s<0) {
375  log_sock_error("creating_socket", -1);
376  return -1;
377  }
378 
379  socklen = tor_addr_to_sockaddr(sockshost, socksport,
380  (struct sockaddr *)&ss, sizeof(ss));
381 
382  if (connect(s, (struct sockaddr*)&ss, socklen)) {
383  log_sock_error("connecting to SOCKS host", s);
384  goto err;
385  }
386 
387  if (version == 5) {
388  socks5_client_version_t *v = socks5_client_version_new();
389 
390  socks5_client_version_set_version(v, 5);
391  socks5_client_version_set_n_methods(v, 1);
392  socks5_client_version_setlen_methods(v, 1);
393  socks5_client_version_set_methods(v, 0, 0x00);
394 
395  tor_assert(!socks5_client_version_check(v));
396  ssize_t encoded_len = socks5_client_version_encoded_len(v);
397  tor_assert(encoded_len > 0);
398 
399  uint8_t *buf = tor_malloc(encoded_len);
400  encoded_len = socks5_client_version_encode(buf, encoded_len, v);
401  tor_assert(encoded_len > 0);
402 
403  socks5_client_version_free(v);
404 
405  if (write_all_to_socket(s, (const char *)buf,
406  encoded_len) != encoded_len) {
407  log_err(LD_NET, "Error sending SOCKS5 method list.");
408  tor_free(buf);
409 
410  goto err;
411  }
412 
413  tor_free(buf);
414 
415  uint8_t method_buf[2];
416 
417  if (read_all_from_socket(s, (char *)method_buf, 2) != 2) {
418  log_err(LD_NET, "Error reading SOCKS5 methods.");
419  goto err;
420  }
421 
422  socks5_server_method_t *m;
423  ssize_t parsed = socks5_server_method_parse(&m, method_buf,
424  sizeof(method_buf));
425 
426  if (parsed < 2) {
427  log_err(LD_NET, "Failed to parse SOCKS5 method selection "
428  "message");
429  socks5_server_method_free(m);
430  goto err;
431  }
432 
433  uint8_t method = socks5_server_method_get_method(m);
434 
435  socks5_server_method_free(m);
436 
437  if (method != 0x00) {
438  log_err(LD_NET, "Unrecognized socks authentication method: %u",
439  (unsigned)method);
440  goto err;
441  }
442  }
443 
444  if ((len = build_socks_resolve_request(&req, "", hostname, reverse,
445  version))<0) {
446  log_err(LD_BUG,"Error generating SOCKS request");
447  tor_assert(!req);
448  goto err;
449  }
450  if (write_all_to_socket(s, (const char *)req, len) != len) {
451  log_sock_error("sending SOCKS request", s);
452  tor_free(req);
453  goto err;
454  }
455  tor_free(req);
456 
457  if (version == 4) {
458  char reply_buf[RESPONSE_LEN_4];
459  if (read_all_from_socket(s, reply_buf, RESPONSE_LEN_4) != RESPONSE_LEN_4) {
460  log_err(LD_NET, "Error reading SOCKS4 response.");
461  goto err;
462  }
463  if (parse_socks4a_resolve_response(hostname,
464  reply_buf, RESPONSE_LEN_4,
465  result_addr)<0) {
466  goto err;
467  }
468  } else {
469  uint8_t reply_buf[512];
470 
471  len = read_all_from_socket(s, (char *)reply_buf,
472  sizeof(reply_buf));
473 
474  socks5_server_reply_t *reply;
475 
476  ssize_t parsed = socks5_server_reply_parse(&reply,
477  reply_buf,
478  len);
479  if (parsed == -1) {
480  log_err(LD_NET, "Failed parsing SOCKS5 response");
481  goto err;
482  }
483 
484  if (parsed == -2) {
485  log_err(LD_NET, "Truncated SOCKS5 response");
486  goto err;
487  }
488 
489  /* Give a user some useful feedback about SOCKS5 errors */
490  uint8_t reply_field = socks5_server_reply_get_reply(reply);
491  if (reply_field != 0) {
492  log_warn(LD_NET,"Got SOCKS5 status response '%u': %s",
493  (unsigned)reply_field,
494  socks5_reason_to_string(reply_field));
495  if (reply_field == 4 && !strcasecmpend(hostname, ".onion")) {
496  onion_warning(hostname);
497  }
498 
499  socks5_server_reply_free(reply);
500  goto err;
501  }
502 
503  uint8_t atype = socks5_server_reply_get_atype(reply);
504 
505  if (atype == SOCKS5_ATYPE_IPV4) {
506  /* IPv4 address */
507  tor_addr_from_ipv4h(result_addr,
508  socks5_server_reply_get_bind_addr_ipv4(reply));
509  } else if (atype == SOCKS5_ATYPE_IPV6) {
510  /* IPv6 address */
511  tor_addr_from_ipv6_bytes(result_addr,
512  socks5_server_reply_getarray_bind_addr_ipv6(reply));
513  } else if (atype == SOCKS5_ATYPE_HOSTNAME) {
514  /* Domain name */
515  domainname_t *dn =
516  socks5_server_reply_get_bind_addr_domainname(reply);
517 
518  *result_hostname = tor_strdup(domainname_getstr_name(dn));
519  }
520 
521  socks5_server_reply_free(reply);
522  }
523 
524  tor_close_socket(s);
525  return 0;
526  err:
527  tor_close_socket(s);
528  return -1;
529 }
530 
531 /** Print a usage message and exit. */
532 static void
533 usage(void)
534 {
535  puts("Syntax: tor-resolve [-4] [-5] [-v] [-x] [-p port] "
536  "hostname [sockshost[:socksport]]");
537  exit(1);
538 }
539 
540 /** Entry point to tor-resolve */
541 int
542 main(int argc, char **argv)
543 {
544  tor_addr_t sockshost;
545  uint16_t socksport = 0, port_option = 0;
546  int isSocks4 = 0, isVerbose = 0, isReverse = 0;
547  char **arg;
548  int n_args;
549  tor_addr_t result;
550  char *result_hostname = NULL;
551 
552  init_logging(1);
553  sandbox_disable_getaddrinfo_cache();
554 
555  arg = &argv[1];
556  n_args = argc-1;
557 
558  if (!n_args)
559  usage();
560 
561  if (!strcmp(arg[0],"--version")) {
562  printf("Tor version %s.\n",VERSION);
563  return 0;
564  }
565  while (n_args && *arg[0] == '-') {
566  if (!strcmp("-v", arg[0]))
567  isVerbose = 1;
568  else if (!strcmp("-4", arg[0]))
569  isSocks4 = 1;
570  else if (!strcmp("-5", arg[0]))
571  isSocks4 = 0;
572  else if (!strcmp("-x", arg[0]))
573  isReverse = 1;
574  else if (!strcmp("-p", arg[0])) {
575  int p;
576  if (n_args < 2) {
577  fprintf(stderr, "No arguments given to -p\n");
578  usage();
579  }
580  p = atoi(arg[1]);
581  if (p<1 || p > 65535) {
582  fprintf(stderr, "-p requires a number between 1 and 65535\n");
583  usage();
584  }
585  port_option = (uint16_t) p;
586  ++arg; /* skip the port */
587  --n_args;
588  } else {
589  fprintf(stderr, "Unrecognized flag '%s'\n", arg[0]);
590  usage();
591  }
592  ++arg;
593  --n_args;
594  }
595 
596  if (isSocks4 && isReverse) {
597  fprintf(stderr, "Reverse lookups not supported with SOCKS4a\n");
598  usage();
599  }
600 
601  log_severity_list_t *severities =
602  tor_malloc_zero(sizeof(log_severity_list_t));
603  if (isVerbose)
605  else
607  add_stream_log(severities, "<stderr>", fileno(stderr));
608  tor_free(severities);
609 
610  if (n_args == 1) {
611  log_debug(LD_CONFIG, "defaulting to localhost");
612  tor_addr_from_ipv4h(&sockshost, 0x7f000001u); /* localhost */
613  if (port_option) {
614  log_debug(LD_CONFIG, "Using port %d", (int)port_option);
615  socksport = port_option;
616  } else {
617  log_debug(LD_CONFIG, "defaulting to port 9050");
618  socksport = 9050; /* 9050 */
619  }
620  } else if (n_args == 2) {
621  if (tor_addr_port_lookup(arg[1], &sockshost, &socksport)<0) {
622  fprintf(stderr, "Couldn't parse/resolve address %s", arg[1]);
623  return 1;
624  }
625  if (socksport && port_option && socksport != port_option) {
626  log_warn(LD_CONFIG, "Conflicting ports; using %d, not %d",
627  (int)socksport, (int)port_option);
628  } else if (port_option) {
629  socksport = port_option;
630  } else if (!socksport) {
631  log_debug(LD_CONFIG, "defaulting to port 9050");
632  socksport = 9050;
633  }
634  } else {
635  usage();
636  }
637 
638  if (network_init()<0) {
639  log_err(LD_BUG,"Error initializing network; exiting.");
640  return 1;
641  }
642 
643  if (do_resolve(arg[0], &sockshost, socksport, isReverse,
644  isSocks4 ? 4 : 5, &result,
645  &result_hostname))
646  return 1;
647 
648  if (result_hostname) {
649  printf("%s\n", result_hostname);
650  } else {
651  printf("%s\n", fmt_addr(&result));
652  }
653  return 0;
654 }
socklen_t tor_addr_to_sockaddr(const tor_addr_t *a, uint16_t port, struct sockaddr *sa_out, socklen_t len)
Definition: address.c:113
void tor_addr_make_unspec(tor_addr_t *a)
Definition: address.c:225
int tor_addr_parse(tor_addr_t *addr, const char *src)
Definition: address.c:1349
void tor_addr_from_ipv6_bytes(tor_addr_t *dest, const uint8_t *ipv6_bytes)
Definition: address.c:900
Headers for address.h.
static sa_family_t tor_addr_family(const tor_addr_t *a)
Definition: address.h:187
static uint32_t tor_addr_to_ipv4h(const tor_addr_t *a)
Definition: address.h:160
#define tor_addr_to_in6_addr8(x)
Definition: address.h:135
#define tor_addr_from_ipv4h(dest, v4addr)
Definition: address.h:327
#define fmt_addr(a)
Definition: address.h:239
Inline functions for reading and writing multibyte values from the middle of strings,...
void init_logging(int disable_startup_queue)
Definition: log.c:911
void set_log_severity_config(int loglevelMin, int loglevelMax, log_severity_list_t *severity_out)
Definition: log.c:868
void add_stream_log(const log_severity_list_t *severity, const char *name, int fd)
Definition: log.c:902
Headers for log.c.
#define LD_PROTOCOL
Definition: log.h:72
#define LOG_DEBUG
Definition: log.h:42
#define LOG_ERR
Definition: log.h:56
#define LD_BUG
Definition: log.h:86
#define LD_NET
Definition: log.h:66
#define LD_GENERAL
Definition: log.h:62
#define LD_CONFIG
Definition: log.h:68
#define LOG_WARN
Definition: log.h:53
Headers for util_malloc.c.
#define tor_free(p)
Definition: malloc.h:52
int tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, uint16_t *port_out)
Definition: resolve.c:252
Header for resolve.c.
Header file for sandbox.c.
int tor_close_socket(tor_socket_t s)
Definition: socket.c:217
ssize_t read_all_from_socket(tor_socket_t sock, char *buf, size_t count)
Definition: socket.c:587
ssize_t write_all_to_socket(tor_socket_t fd, const char *buf, size_t count)
Definition: socket.c:611
int network_init(void)
Definition: socket.c:41
tor_socket_t tor_open_socket(int domain, int type, int protocol)
Definition: socket.c:243
Header for socket.c.
Status codes used by the SOCKS5 protocol.
int main(int argc, char *argv[])
Definition: tor_main.c:25
#define tor_assert(expr)
Definition: util_bug.h:102
int strcasecmpend(const char *s1, const char *s2)
Definition: util_string.c:264
Header for util_string.c.