buffers.c 20.7 KB
Newer Older
Roger Dingledine's avatar
Roger Dingledine committed
1
/* Copyright 2001 Matej Pfajfar, 2001-2004 Roger Dingledine. */
2
3
/* See LICENSE for licensing information */
/* $Id$ */
Roger Dingledine's avatar
Roger Dingledine committed
4

5
6
7
8
/**
 * \file buffers.c
 * \brief Abstractions for buffered IO.
 **/
Roger Dingledine's avatar
Roger Dingledine committed
9
10
11

#include "or.h"

12
#define BUFFER_MAGIC 0xB0FFF312u
13
struct buf_t {
14
15
  uint32_t magic; /**< Magic cookie for debugging: Must be set to BUFFER_MAGIC */
  char *mem;      /**< Storage for data in the buffer */
Roger Dingledine's avatar
Roger Dingledine committed
16
17
  size_t len;     /**< Maximum amount of data that <b>mem</b> can hold. */
  size_t datalen; /**< Number of bytes currently in <b>mem</b>. */
18
};
19

20
/** Size, in bytes, for newly allocated buffers.  Should be a power of 2. */
21
#define INITIAL_BUF_SIZE (4*1024)
22
/** Maximum size, in bytes, for resized buffers. */
23
#define MAX_BUF_SIZE (1024*1024*10)
24
/** Size, in bytes, for minimum 'shrink' size for buffers.  Buffers may start
25
26
 * out smaller than this, but they will never autoshrink to less
 * than this size. */
27
#define MIN_BUF_SHRINK_SIZE (16*1024)
28

Roger Dingledine's avatar
Roger Dingledine committed
29
/** Change a buffer's capacity. <b>new_capacity</b> must be \<= buf->datalen. */
30
31
static INLINE void buf_resize(buf_t *buf, size_t new_capacity)
{
Roger Dingledine's avatar
Roger Dingledine committed
32
33
  tor_assert(buf->datalen <= new_capacity);
  tor_assert(new_capacity);
Roger Dingledine's avatar
Roger Dingledine committed
34
  buf->mem = tor_realloc(buf->mem, new_capacity);
35
36
37
  buf->len = new_capacity;
}

Roger Dingledine's avatar
Roger Dingledine committed
38
/** If the buffer is not large enough to hold <b>capacity</b> bytes, resize
39
40
41
42
43
44
 * it so that it can.  (The new size will be a power of 2 times the old
 * size.)
 */
static INLINE int buf_ensure_capacity(buf_t *buf, size_t capacity)
{
  size_t new_len;
45
  if (buf->len >= capacity)  /* Don't grow if we're already big enough. */
46
    return 0;
47
  if (capacity > MAX_BUF_SIZE) /* Don't grow past the maximum. */
48
    return -1;
49
50
51
  /* Find the smallest new_len equal to (2**X)*len for some X; such that
   * new_len is at least capacity.
   */
52
53
54
  new_len = buf->len*2;
  while (new_len < capacity)
    new_len *= 2;
55
  /* Resize the buffer. */
56
57
  log_fn(LOG_DEBUG,"Growing buffer from %d to %d bytes.",
         (int)buf->len, (int)new_len);
58
59
60
61
  buf_resize(buf,new_len);
  return 0;
}

62
/** If the buffer is at least 2*MIN_BUF_SHRINK_SIZE bytes in capacity,
63
64
65
66
67
68
 * and if the buffer is less than 1/4 full, shrink the buffer until
 * one of the above no longer holds.  (We shrink the buffer by
 * dividing by powers of 2.)
 */
static INLINE void buf_shrink_if_underfull(buf_t *buf) {
  size_t new_len;
69
70
71
  /* If the buffer is at least .25 full, or if shrinking the buffer would
   * put it onder MIN_BUF_SHRINK_SIZE, don't do it. */
  if (buf->datalen >= buf->len/4 || buf->len < 2*MIN_BUF_SHRINK_SIZE)
72
    return;
73
74
75
76
  /* Shrink new_len by powers of 2 until: datalen is at least 1/4 of
   * new_len, OR shrinking new_len more would put it under
   * MIN_BUF_SHRINK_SIZE.
   */
77
  new_len = buf->len / 2;
Roger Dingledine's avatar
Roger Dingledine committed
78
  while (buf->datalen < new_len/4 && new_len/2 > MIN_BUF_SHRINK_SIZE)
79
    new_len /= 2;
80
81
82
  log_fn(LOG_DEBUG,"Shrinking buffer from %d to %d bytes.",
         (int)buf->len, (int)new_len);
  buf_resize(buf, new_len);
83
84
}

Roger Dingledine's avatar
Roger Dingledine committed
85
/** Remove the first <b>n</b> bytes from buf.
86
87
 */
static INLINE void buf_remove_from_front(buf_t *buf, size_t n) {
Roger Dingledine's avatar
Roger Dingledine committed
88
  tor_assert(buf->datalen >= n);
89
  buf->datalen -= n;
Roger Dingledine's avatar
Roger Dingledine committed
90
  memmove(buf->mem, buf->mem+n, buf->datalen);
91
92
93
  buf_shrink_if_underfull(buf);
}

Roger Dingledine's avatar
Roger Dingledine committed
94
95
/** Find the first instance of the str_len byte string <b>str</b> on the
 * buf_len byte string <b>bufstr</b>.  Strings are not necessary
96
97
 * NUL-terminated. If none exists, return -1.  Otherwise, return index
 * of the first character in bufstr _after_ the first instance of str.
98
 */
99
100
101
102
/* XXXX The way this function is used, we could always get away with
 * XXXX assuming that str is NUL terminated, and use strstr instead. */
static int find_mem_in_mem(const char *str, int str_len,
                           const char *bufstr, int buf_len)
103
104
{
  const char *location;
105
  const char *last_possible = bufstr + buf_len - str_len;
106

107
  tor_assert(str && str_len > 0 && bufstr);
108
109
110
111

  if(buf_len < str_len)
    return -1;

112
  for(location = bufstr; location <= last_possible; location++)
113
    if((*location == *str) && !memcmp(location+1, str+1, str_len-1))
114
      return location-bufstr+str_len;
115
116
117
118

  return -1;
}

Roger Dingledine's avatar
Roger Dingledine committed
119
/** Create and return a new buf with capacity <b>size</b>.
120
 */
121
buf_t *buf_new_with_capacity(size_t size) {
122
  buf_t *buf;
123
  buf = tor_malloc(sizeof(buf_t));
124
  buf->magic = BUFFER_MAGIC;
125
  buf->mem = tor_malloc(size);
126
127
  buf->len = size;
  buf->datalen = 0;
Roger Dingledine's avatar
Roger Dingledine committed
128
//  memset(buf->mem,0,size);
129

Roger Dingledine's avatar
Roger Dingledine committed
130
  assert_buf_ok(buf);
131
132
  return buf;
}
Roger Dingledine's avatar
Roger Dingledine committed
133

134
/** Allocate and return a new buffer with default capacity. */
135
136
buf_t *buf_new()
{
137
  return buf_new_with_capacity(INITIAL_BUF_SIZE);
138
}
Roger Dingledine's avatar
Roger Dingledine committed
139

Roger Dingledine's avatar
Roger Dingledine committed
140
/** Remove all data from <b>buf</b> */
141
142
143
144
void buf_clear(buf_t *buf)
{
  buf->datalen = 0;
}
Roger Dingledine's avatar
Roger Dingledine committed
145

Roger Dingledine's avatar
Roger Dingledine committed
146
/** Return the number of bytes stored in <b>buf</b> */
147
148
149
size_t buf_datalen(const buf_t *buf)
{
  return buf->datalen;
Roger Dingledine's avatar
Roger Dingledine committed
150
151
}

Roger Dingledine's avatar
Roger Dingledine committed
152
/** Return the maximum bytes that can be stored in <b>buf</b> before buf
153
 * needs to resize. */
154
155
156
157
158
size_t buf_capacity(const buf_t *buf)
{
  return buf->len;
}

Roger Dingledine's avatar
Roger Dingledine committed
159
/** For testing only: Return a pointer to the raw memory stored in <b>buf</b>.
160
 */
161
162
const char *_buf_peek_raw_buffer(const buf_t *buf)
{
Roger Dingledine's avatar
Roger Dingledine committed
163
  return buf->mem;
164
165
}

Roger Dingledine's avatar
Roger Dingledine committed
166
/** Release storage held by <b>buf</b>.
167
 */
168
void buf_free(buf_t *buf) {
169
170
171
172
  assert_buf_ok(buf);
  buf->magic = 0xDEADBEEF;
  tor_free(buf->mem);
  tor_free(buf);
Roger Dingledine's avatar
Roger Dingledine committed
173
174
}

Roger Dingledine's avatar
Roger Dingledine committed
175
176
177
/** Read from socket <b>s</s>, writing onto end of <b>buf</b>.  Read at most
 * <b>at_most</b> bytes, resizing the buffer as necessary.  If read()
 * returns 0, set <b>*reached_eof</b> to 1 and return 0. Return -1 on error;
178
179
 * else return the number of bytes read.  Return 0 if read() would
 * block.
180
 */
181
int read_to_buf(int s, size_t at_most, buf_t *buf, int *reached_eof) {
Roger Dingledine's avatar
Roger Dingledine committed
182
183
184

  int read_result;

Roger Dingledine's avatar
Roger Dingledine committed
185
186
  assert_buf_ok(buf);
  tor_assert(reached_eof && (s>=0));
Roger Dingledine's avatar
Roger Dingledine committed
187

188
189
  if (buf_ensure_capacity(buf,buf->datalen+at_most))
    return -1;
Roger Dingledine's avatar
Roger Dingledine committed
190

191
  if(at_most + buf->datalen > buf->len)
192
    at_most = buf->len - buf->datalen; /* take the min of the two */
193
194
195
196

  if(at_most == 0)
    return 0; /* we shouldn't read anything */

197
//  log_fn(LOG_DEBUG,"reading at most %d bytes.",at_most);
198
  read_result = recv(s, buf->mem+buf->datalen, at_most, 0);
Roger Dingledine's avatar
Roger Dingledine committed
199
  if (read_result < 0) {
200
    if(!ERRNO_IS_EAGAIN(tor_socket_errno(s))) { /* it's a real error */
Roger Dingledine's avatar
Roger Dingledine committed
201
202
      return -1;
    }
203
    return 0; /* would block. */
Roger Dingledine's avatar
Roger Dingledine committed
204
  } else if (read_result == 0) {
205
    log_fn(LOG_DEBUG,"Encountered eof");
206
    *reached_eof = 1;
Roger Dingledine's avatar
Roger Dingledine committed
207
208
    return 0;
  } else { /* we read some bytes */
209
210
211
    buf->datalen += read_result;
    log_fn(LOG_DEBUG,"Read %d bytes. %d on inbuf.",read_result,
           (int)buf->datalen);
Roger Dingledine's avatar
Roger Dingledine committed
212
213
214
215
    return read_result;
  }
}

216
/** As read_to_buf, but reads from a TLS connection.
217
 */
218
int read_to_buf_tls(tor_tls *tls, size_t at_most, buf_t *buf) {
219
  int r;
Roger Dingledine's avatar
Roger Dingledine committed
220
221
  tor_assert(tls);
  assert_buf_ok(buf);
222

223
224
225
  log_fn(LOG_DEBUG,"start: %d on buf, %d pending, at_most %d.",
         (int)buf_datalen(buf), (int)tor_tls_get_pending_bytes(tls),
         (int)at_most);
Roger Dingledine's avatar
Roger Dingledine committed
226

227
  if (buf_ensure_capacity(buf, at_most+buf->datalen))
228
    return TOR_TLS_ERROR;
229

230
  if (at_most + buf->datalen > buf->len)
231
    at_most = buf->len - buf->datalen;
232
233
234

  if (at_most == 0)
    return 0;
235

236
237
238
  log_fn(LOG_DEBUG,"before: %d on buf, %d pending, at_most %d.",
         (int)buf_datalen(buf), (int)tor_tls_get_pending_bytes(tls),
         (int)at_most);
239
240

  assert_no_tls_errors();
Roger Dingledine's avatar
Roger Dingledine committed
241
  r = tor_tls_read(tls, buf->mem+buf->datalen, at_most);
Roger Dingledine's avatar
Roger Dingledine committed
242
  if (r<0)
243
    return r;
244
  buf->datalen += r;
245
246
  log_fn(LOG_DEBUG,"Read %d bytes. %d on inbuf; %d pending",r,
         (int)buf->datalen,(int)tor_tls_get_pending_bytes(tls));
247
  return r;
Roger Dingledine's avatar
Roger Dingledine committed
248
}
249

Roger Dingledine's avatar
Roger Dingledine committed
250
251
252
253
254
/** Write data from <b>buf</b> to the socket <b>s</b>.  Write at most
 * <b>*buf_flushlen</b> bytes, decrement <b>*buf_flushlen</b> by
 * the number of bytes actually written, and remove the written bytes
 * from the buffer.  Return the number of bytes written on success,
 * -1 on failure.  Return 0 if write() would block.
255
 */
Roger Dingledine's avatar
Roger Dingledine committed
256
int flush_buf(int s, buf_t *buf, int *buf_flushlen)
257
{
Roger Dingledine's avatar
Roger Dingledine committed
258
259
  int write_result;

Roger Dingledine's avatar
Roger Dingledine committed
260
  assert_buf_ok(buf);
261
  tor_assert(buf_flushlen && (s>=0) && ((unsigned)*buf_flushlen <= buf->datalen));
Roger Dingledine's avatar
Roger Dingledine committed
262

263
  if(*buf_flushlen == 0) /* nothing to flush */
Roger Dingledine's avatar
Roger Dingledine committed
264
265
    return 0;

266
  write_result = send(s, buf->mem, *buf_flushlen, 0);
Roger Dingledine's avatar
Roger Dingledine committed
267
  if (write_result < 0) {
268
269
270
    if(!ERRNO_IS_EAGAIN(tor_socket_errno(s))) { /* it's a real error */
      /* get a stack trace to find epipe bugs */
      tor_assert(tor_socket_errno(s) != EPIPE);
271
272
      return -1;
    }
273
    log_fn(LOG_DEBUG,"write() would block, returning.");
Roger Dingledine's avatar
Roger Dingledine committed
274
275
    return 0;
  } else {
276
    *buf_flushlen -= write_result;
277
    buf_remove_from_front(buf, write_result);
278
    log_fn(LOG_DEBUG,"%d: flushed %d bytes, %d ready to flush, %d remain.",
279
           s,write_result,*buf_flushlen,(int)buf->datalen);
280

281
    return write_result;
Roger Dingledine's avatar
Roger Dingledine committed
282
283
284
  }
}

285
/** As flush_buf, but writes data to a TLS connection.
286
 */
Roger Dingledine's avatar
Roger Dingledine committed
287
int flush_buf_tls(tor_tls *tls, buf_t *buf, int *buf_flushlen)
288
289
{
  int r;
Roger Dingledine's avatar
Roger Dingledine committed
290
291
  assert_buf_ok(buf);
  tor_assert(tls && buf_flushlen);
Roger Dingledine's avatar
Roger Dingledine committed
292
293
294

  /* we want to let tls write even if flushlen is zero, because it might
   * have a partial record pending */
Roger Dingledine's avatar
Roger Dingledine committed
295
  r = tor_tls_write(tls, buf->mem, *buf_flushlen);
296
297
298
299
  if (r < 0) {
    return r;
  }
  *buf_flushlen -= r;
300
  buf_remove_from_front(buf, r);
301
  log_fn(LOG_DEBUG,"flushed %d bytes, %d ready to flush, %d remain.",
302
    r,*buf_flushlen,(int)buf->datalen);
303
304
305
  return r;
}

Roger Dingledine's avatar
Roger Dingledine committed
306
307
308
/** Append <b>string_len</b> bytes from <b>string</b> to the end of
 * <b>buf</b>.
 *
309
310
 * Return the new length of the buffer on success, -1 on failure.
 */
311
int write_to_buf(const char *string, int string_len, buf_t *buf) {
Roger Dingledine's avatar
Roger Dingledine committed
312
313
314
315
316

  /* append string to buf (growing as needed, return -1 if "too big")
   * return total number of bytes on the buf
   */

Roger Dingledine's avatar
Roger Dingledine committed
317
318
  tor_assert(string);
  assert_buf_ok(buf);
Roger Dingledine's avatar
Roger Dingledine committed
319

320
  if (buf_ensure_capacity(buf, buf->datalen+string_len)) {
321
    log_fn(LOG_WARN, "buflen too small, can't hold %d bytes.", (int)buf->datalen+string_len);
322
    return -1;
323
  }
324

Roger Dingledine's avatar
Roger Dingledine committed
325
  memcpy(buf->mem+buf->datalen, string, string_len);
326
327
328
  buf->datalen += string_len;
  log_fn(LOG_DEBUG,"added %d bytes to buf (now %d total).",string_len, (int)buf->datalen);
  return buf->datalen;
329
330
}

Roger Dingledine's avatar
Roger Dingledine committed
331
332
/** Remove <b>string_len</b> bytes from the front of <b>buf</b>, and store them
 * into <b>string</b>.  Return the new buffer size.  <b>string_len</b> must be \<=
333
334
 * the number of bytes on the buffer.
 */
335
int fetch_from_buf(char *string, size_t string_len, buf_t *buf) {
Roger Dingledine's avatar
Roger Dingledine committed
336

337
  /* There must be string_len bytes in buf; write them onto string,
338
339
340
   * then memmove buf back (that is, remove them from buf).
   *
   * Return the number of bytes still on the buffer. */
Roger Dingledine's avatar
Roger Dingledine committed
341

Roger Dingledine's avatar
Roger Dingledine committed
342
343
344
  tor_assert(string);
  tor_assert(string_len <= buf->datalen); /* make sure we don't ask for too much */
  assert_buf_ok(buf);
Roger Dingledine's avatar
Roger Dingledine committed
345

Roger Dingledine's avatar
Roger Dingledine committed
346
  memcpy(string,buf->mem,string_len);
347
  buf_remove_from_front(buf, string_len);
348
  return buf->datalen;
Roger Dingledine's avatar
Roger Dingledine committed
349
350
}

Roger Dingledine's avatar
Roger Dingledine committed
351
/** There is a (possibly incomplete) http statement on <b>buf</b>, of the
352
 * form "\%s\\r\\n\\r\\n\%s", headers, body. (body may contain nuls.)
353
354
355
 * If a) the headers include a Content-Length field and all bytes in
 * the body are present, or b) there's no Content-Length field and
 * all headers are present, then:
356
 *
Roger Dingledine's avatar
Roger Dingledine committed
357
358
359
 *  - strdup headers into <b>*headers_out</b>, and nul-terminate it.
 *  - memdup body into <b>*body_out</b>, and nul-terminate it.
 *  - Then remove them from <b>buf</b>, and return 1.
360
361
362
 *
 *  - If headers or body is NULL, discard that part of the buf.
 *  - If a headers or body doesn't fit in the arg, return -1.
Roger Dingledine's avatar
Roger Dingledine committed
363
 *
364
365
 * Else, change nothing and return 0.
 */
366
int fetch_from_buf_http(buf_t *buf,
367
                        char **headers_out, int max_headerlen,
368
                        char **body_out, int *body_used, int max_bodylen) {
369
370
371
372
  char *headers, *body;
  int i;
  int headerlen, bodylen, contentlen;

Roger Dingledine's avatar
Roger Dingledine committed
373
  assert_buf_ok(buf);
374

Roger Dingledine's avatar
Roger Dingledine committed
375
  headers = buf->mem;
376
  i = find_mem_in_mem("\r\n\r\n", 4, buf->mem, buf->datalen);
377
378
379
380
  if(i < 0) {
    log_fn(LOG_DEBUG,"headers not all here yet.");
    return 0;
  }
Roger Dingledine's avatar
Roger Dingledine committed
381
  body = buf->mem+i;
382
  headerlen = body-headers; /* includes the CRLFCRLF */
383
  bodylen = buf->datalen - headerlen;
384
  log_fn(LOG_DEBUG,"headerlen %d, bodylen %d.", headerlen, bodylen);
385
386

  if(headers_out && max_headerlen <= headerlen) {
Roger Dingledine's avatar
Roger Dingledine committed
387
    log_fn(LOG_WARN,"headerlen %d larger than %d. Failing.", headerlen, max_headerlen-1);
388
389
390
    return -1;
  }
  if(body_out && max_bodylen <= bodylen) {
Roger Dingledine's avatar
Roger Dingledine committed
391
    log_fn(LOG_WARN,"bodylen %d larger than %d. Failing.", bodylen, max_bodylen-1);
392
393
394
    return -1;
  }

395
#define CONTENT_LENGTH "\r\nContent-Length: "
396
  i = find_mem_in_mem(CONTENT_LENGTH, strlen(CONTENT_LENGTH),
397
                      headers, headerlen);
398
399
  if(i > 0) {
    contentlen = atoi(headers+i);
400
    /* if content-length is malformed, then our body length is 0. fine. */
401
    log_fn(LOG_DEBUG,"Got a contentlen of %d.",contentlen);
402
403
404
405
    if(bodylen < contentlen) {
      log_fn(LOG_DEBUG,"body not all here yet.");
      return 0; /* not all there yet */
    }
406
407
408
409
    if(bodylen > contentlen) {
      bodylen = contentlen;
      log_fn(LOG_DEBUG,"bodylen reduced to %d.",bodylen);
    }
410
411
412
  }
  /* all happy. copy into the appropriate places, and return 1 */
  if(headers_out) {
413
414
    *headers_out = tor_malloc(headerlen+1);
    memcpy(*headers_out,buf->mem,headerlen);
415
    (*headers_out)[headerlen] = 0; /* null terminate it */
416
417
  }
  if(body_out) {
Roger Dingledine's avatar
Roger Dingledine committed
418
    tor_assert(body_used);
419
    *body_used = bodylen;
420
421
    *body_out = tor_malloc(bodylen+1);
    memcpy(*body_out,buf->mem+headerlen,bodylen);
422
    (*body_out)[bodylen] = 0; /* null terminate it */
423
  }
424
  buf_remove_from_front(buf, headerlen+bodylen);
425
426
427
  return 1;
}

Roger Dingledine's avatar
Roger Dingledine committed
428
/** There is a (possibly incomplete) socks handshake on <b>buf</b>, of one
Roger Dingledine's avatar
Roger Dingledine committed
429
 * of the forms
430
431
432
433
 *  - socks4: "socksheader username\\0"
 *  - socks4a: "socksheader username\\0 destaddr\\0"
 *  - socks5 phase one: "version #methods methods"
 *  - socks5 phase two: "version command 0 addresstype..."
434
435
 * If it's a complete and valid handshake, and destaddr fits in
 *   MAX_SOCKS_ADDR_LEN bytes, then pull the handshake off the buf,
Roger Dingledine's avatar
Roger Dingledine committed
436
 *   assign to <b>req</b>, and return 1.
437
 *
438
 * If it's invalid or too big, return -1.
439
 *
Roger Dingledine's avatar
Roger Dingledine committed
440
 * Else it's not all there yet, leave buf alone and return 0.
441
 *
Roger Dingledine's avatar
Roger Dingledine committed
442
443
 * If you want to specify the socks reply, write it into <b>req->reply</b>
 *   and set <b>req->replylen</b>, else leave <b>req->replylen</b> alone.
444
 *
Roger Dingledine's avatar
Roger Dingledine committed
445
 * If returning 0 or -1, <b>req->address</b> and <b>req->port</b> are undefined.
446
 */
447
int fetch_from_buf_socks(buf_t *buf, socks_request_t *req) {
Roger Dingledine's avatar
Roger Dingledine committed
448
  unsigned char len;
449
  char *tmpbuf=NULL;
Roger Dingledine's avatar
Roger Dingledine committed
450
451
  uint32_t destip;
  enum {socks4, socks4a} socks4_prot = socks4a;
452
  char *next, *startaddr;
Roger Dingledine's avatar
Roger Dingledine committed
453
  struct in_addr in;
454

Roger Dingledine's avatar
Roger Dingledine committed
455
  if(buf->datalen < 2) /* version and another byte */
456
    return 0;
Roger Dingledine's avatar
Roger Dingledine committed
457
  switch(*(buf->mem)) { /* which version of socks? */
Roger Dingledine's avatar
Roger Dingledine committed
458
459
460

    case 5: /* socks5 */

461
      if(req->socks_version != 5) { /* we need to negotiate a method */
Roger Dingledine's avatar
Roger Dingledine committed
462
        unsigned char nummethods = (unsigned char)*(buf->mem+1);
Roger Dingledine's avatar
Roger Dingledine committed
463
        tor_assert(!req->socks_version);
464
        if(buf->datalen < 2u+nummethods)
Roger Dingledine's avatar
Roger Dingledine committed
465
          return 0;
Roger Dingledine's avatar
Roger Dingledine committed
466
        if(!nummethods || !memchr(buf->mem+2, 0, nummethods)) {
Roger Dingledine's avatar
Roger Dingledine committed
467
          log_fn(LOG_WARN,"socks5: offered methods don't include 'no auth'. Rejecting.");
468
469
          req->replylen = 2; /* 2 bytes of response */
          req->reply[0] = 5; /* socks5 reply */
470
          req->reply[1] = '\xFF'; /* reject all methods */
Roger Dingledine's avatar
Roger Dingledine committed
471
          return -1;
Roger Dingledine's avatar
Roger Dingledine committed
472
        }
473
        buf_remove_from_front(buf,2+nummethods);/* remove packet from buf */
Roger Dingledine's avatar
Roger Dingledine committed
474

475
476
477
478
        req->replylen = 2; /* 2 bytes of response */
        req->reply[0] = 5; /* socks5 reply */
        req->reply[1] = 0; /* choose the 'no auth' method */
        req->socks_version = 5; /* remember that we've already negotiated auth */
Roger Dingledine's avatar
Roger Dingledine committed
479
480
481
482
483
484
485
        log_fn(LOG_DEBUG,"socks5: accepted method 0");
        return 0;
      }
      /* we know the method; read in the request */
      log_fn(LOG_DEBUG,"socks5: checking request");
      if(buf->datalen < 8) /* basic info plus >=2 for addr plus 2 for port */
        return 0; /* not yet */
Roger Dingledine's avatar
Roger Dingledine committed
486
      if(*(buf->mem+1) != 1) { /* not a connect? we don't support it. */
487
        log_fn(LOG_WARN,"socks5: command %d not '1'. Rejecting.",*(buf->mem+1));
Roger Dingledine's avatar
Roger Dingledine committed
488
489
        return -1;
      }
Roger Dingledine's avatar
Roger Dingledine committed
490
      switch(*(buf->mem+3)) { /* address type */
Roger Dingledine's avatar
Roger Dingledine committed
491
492
493
494
        case 1: /* IPv4 address */
          log_fn(LOG_DEBUG,"socks5: ipv4 address type");
          if(buf->datalen < 10) /* ip/port there? */
            return 0; /* not yet */
Roger Dingledine's avatar
Roger Dingledine committed
495
          destip = ntohl(*(uint32_t*)(buf->mem+4));
Roger Dingledine's avatar
Roger Dingledine committed
496
497
          in.s_addr = htonl(destip);
          tmpbuf = inet_ntoa(in);
498
          if(strlen(tmpbuf)+1 > MAX_SOCKS_ADDR_LEN) {
499
            log_fn(LOG_WARN,"socks5 IP takes %d bytes, which doesn't fit in %d. Rejecting.",
500
                   (int)strlen(tmpbuf)+1,(int)MAX_SOCKS_ADDR_LEN);
Roger Dingledine's avatar
Roger Dingledine committed
501
502
            return -1;
          }
Roger Dingledine's avatar
Roger Dingledine committed
503
          strcpy(req->address,tmpbuf);
504
          req->port = ntohs(*(uint16_t*)(buf->mem+8));
505
          buf_remove_from_front(buf, 10);
Roger Dingledine's avatar
Roger Dingledine committed
506
507
508
          return 1;
        case 3: /* fqdn */
          log_fn(LOG_DEBUG,"socks5: fqdn address type");
Roger Dingledine's avatar
Roger Dingledine committed
509
          len = (unsigned char)*(buf->mem+4);
510
          if(buf->datalen < 7u+len) /* addr/port there? */
Roger Dingledine's avatar
Roger Dingledine committed
511
            return 0; /* not yet */
512
          if(len+1 > MAX_SOCKS_ADDR_LEN) {
513
            log_fn(LOG_WARN,"socks5 hostname is %d bytes, which doesn't fit in %d. Rejecting.",
514
                   len+1,MAX_SOCKS_ADDR_LEN);
Roger Dingledine's avatar
Roger Dingledine committed
515
516
            return -1;
          }
Roger Dingledine's avatar
Roger Dingledine committed
517
518
          memcpy(req->address,buf->mem+5,len);
          req->address[len] = 0;
519
          req->port = ntohs(get_uint16(buf->mem+5+len));
520
          buf_remove_from_front(buf, 5+len+2);
Roger Dingledine's avatar
Roger Dingledine committed
521
522
          return 1;
        default: /* unsupported */
523
          log_fn(LOG_WARN,"socks5: unsupported address type %d. Rejecting.",*(buf->mem+3));
Roger Dingledine's avatar
Roger Dingledine committed
524
525
          return -1;
      }
Roger Dingledine's avatar
Roger Dingledine committed
526
      tor_assert(0);
Roger Dingledine's avatar
Roger Dingledine committed
527
    case 4: /* socks4 */
528
529
      /* http://archive.socks.permeo.com/protocol/socks4.protocol */
      /* http://archive.socks.permeo.com/protocol/socks4a.protocol */
Roger Dingledine's avatar
Roger Dingledine committed
530

531
      req->socks_version = 4;
Roger Dingledine's avatar
Roger Dingledine committed
532
533
534
      if(buf->datalen < SOCKS4_NETWORK_LEN) /* basic info available? */
        return 0; /* not yet */

Roger Dingledine's avatar
Roger Dingledine committed
535
      if(*(buf->mem+1) != 1) { /* not a connect? we don't support it. */
536
        log_fn(LOG_WARN,"socks4: command %d not '1'. Rejecting.",*(buf->mem+1));
Roger Dingledine's avatar
Roger Dingledine committed
537
538
539
        return -1;
      }

540
      req->port = ntohs(*(uint16_t*)(buf->mem+2));
Roger Dingledine's avatar
Roger Dingledine committed
541
      destip = ntohl(*(uint32_t*)(buf->mem+4));
542
      if(!req->port || !destip) {
543
        log_fn(LOG_WARN,"socks4: Port or DestIP is zero. Rejecting.");
Roger Dingledine's avatar
Roger Dingledine committed
544
545
546
547
548
549
        return -1;
      }
      if(destip >> 8) {
        log_fn(LOG_DEBUG,"socks4: destip not in form 0.0.0.x.");
        in.s_addr = htonl(destip);
        tmpbuf = inet_ntoa(in);
550
        if(strlen(tmpbuf)+1 > MAX_SOCKS_ADDR_LEN) {
551
          log_fn(LOG_WARN,"socks4 addr (%d bytes) too long. Rejecting.",
552
                 (int)strlen(tmpbuf));
Roger Dingledine's avatar
Roger Dingledine committed
553
554
555
556
557
558
          return -1;
        }
        log_fn(LOG_DEBUG,"socks4: successfully read destip (%s)", tmpbuf);
        socks4_prot = socks4;
      }

Roger Dingledine's avatar
Roger Dingledine committed
559
      next = memchr(buf->mem+SOCKS4_NETWORK_LEN, 0, buf->datalen);
Roger Dingledine's avatar
Roger Dingledine committed
560
      if(!next) {
561
        log_fn(LOG_DEBUG,"socks4: Username not here yet.");
Roger Dingledine's avatar
Roger Dingledine committed
562
563
564
565
566
        return 0;
      }

      startaddr = next+1;
      if(socks4_prot == socks4a) {
Roger Dingledine's avatar
Roger Dingledine committed
567
        next = memchr(startaddr, 0, buf->mem+buf->datalen-startaddr);
Roger Dingledine's avatar
Roger Dingledine committed
568
        if(!next) {
569
          log_fn(LOG_DEBUG,"socks4: Destaddr not here yet.");
Roger Dingledine's avatar
Roger Dingledine committed
570
571
          return 0;
        }
572
        if(MAX_SOCKS_ADDR_LEN <= next-startaddr) {
573
          log_fn(LOG_WARN,"socks4: Destaddr too long. Rejecting.");
Roger Dingledine's avatar
Roger Dingledine committed
574
575
576
          return -1;
        }
      }
577
      log_fn(LOG_DEBUG,"socks4: Everything is here. Success.");
Roger Dingledine's avatar
Roger Dingledine committed
578
      strcpy(req->address, socks4_prot == socks4 ? tmpbuf : startaddr);
579
580
581
      /* XXX on very old netscapes (socks4) the next line triggers an
       * assert, because next-buf->mem+1 is greater than buf->datalen.
       */
Roger Dingledine's avatar
Roger Dingledine committed
582
      buf_remove_from_front(buf, next-buf->mem+1); /* next points to the final \0 on inbuf */
Roger Dingledine's avatar
Roger Dingledine committed
583
584
      return 1;

585
586
587
588
589
590
    case 'G': /* get */
    case 'H': /* head */
    case 'P': /* put/post */
    case 'C': /* connect */
      strcpy(req->reply,
"HTTP/1.0 501 Tor is not an HTTP Proxy\r\n"
591
"Content-Type: text/html; charset=iso-8859-1\r\n\r\n"
592
593
594
595
596
"<html>\n"
"<head>\n"
"<title>Tor is not an HTTP Proxy</title>\n"
"</head>\n"
"<body>\n"
597
598
"<h1>Tor is not an HTTP Proxy</h1>\n"
"<p>\n"
599
600
601
"It appears you have configured your web browser to use Tor as an HTTP Proxy.\n"
"This is not correct: Tor provides a SOCKS proxy. Please configure your\n"
"client accordingly.\n"
602
603
"</p>\n"
"<p>\n"
604
"See <a href=\"http://freehaven.net/tor/cvs/INSTALL\">http://freehaven.net/tor/cvs/INSTALL</a> for more information.\n"
605
"<!-- Plus this comment, to make the body response more than 512 bytes, so IE will be willing to display it. Comment comment comment comment comment comment comment comment comment comment comment comment.-->\n"
606
"</p>\n"
607
608
609
610
611
"</body>\n"
"</html>\n"
);
      req->replylen = strlen(req->reply)+1;
      /* fall through */
Roger Dingledine's avatar
Roger Dingledine committed
612
    default: /* version is not socks4 or socks5 */
613
      log_fn(LOG_WARN,"Socks version %d not recognized. (Tor is not an http proxy.)",
614
             *(buf->mem));
615
616
617
618
      return -1;
  }
}

Roger Dingledine's avatar
Roger Dingledine committed
619
/** Log an error and exit if <b>buf</b> is corrupted.
620
 */
621
622
void assert_buf_ok(buf_t *buf)
{
Roger Dingledine's avatar
Roger Dingledine committed
623
624
625
626
  tor_assert(buf);
  tor_assert(buf->magic == BUFFER_MAGIC);
  tor_assert(buf->mem);
  tor_assert(buf->datalen <= buf->len);
627
628
}

629
630
631
632
633
634
635
/*
  Local Variables:
  mode:c
  indent-tabs-mode:nil
  c-basic-offset:2
  End:
*/