~ubuntu-branches/ubuntu/utopic/evolution-data-server/utopic-proposed

« back to all changes in this revision

Viewing changes to camel/camel-tcp-stream-raw.c

  • Committer: Package Import Robot
  • Author(s): Iain Lane
  • Date: 2014-06-13 12:02:14 UTC
  • mfrom: (1.1.116) (1.2.35 sid)
  • Revision ID: package-import@ubuntu.com-20140613120214-1zx93d8jxwt093aw
Tags: 3.12.2-1ubuntu1
* Merge with Debian, remaining changes:
  - debian/control: build depend on hardening-wrapper
  - Add build-depends and pass configure flag to enable Ubuntu Online
    Accounts support.
  - Filter out -Bsymbolic-functions from LDFLAGS (for future people
    wondering about this change, see e.g. BGO #594473 and duplicates).
  - Enable Ubuntu Online Accounts and split it and GOA into a separate
    package

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
 
/*
3
 
 *  Authors: Jeffrey Stedfast <fejj@ximian.com>
4
 
 *
5
 
 *  Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
6
 
 *
7
 
 * This program is free software; you can redistribute it and/or
8
 
 * modify it under the terms of version 2 of the GNU Lesser General Public
9
 
 * License as published by the Free Software Foundation.
10
 
 *
11
 
 * This program is distributed in the hope that it will be useful,
12
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 
 * General Public License for more details.
15
 
 *
16
 
 * You should have received a copy of the GNU Lesser General Public
17
 
 * License along with this program; if not, write to the
18
 
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19
 
 * Boston, MA 02110-1301, USA.
20
 
 *
21
 
 */
22
 
 
23
 
#ifdef HAVE_CONFIG_H
24
 
#include <config.h>
25
 
#endif
26
 
 
27
 
#include <errno.h>
28
 
#include <fcntl.h>
29
 
#include <stdio.h>
30
 
#include <string.h>
31
 
#include <unistd.h>
32
 
#include <sys/time.h>
33
 
#include <sys/types.h>
34
 
 
35
 
#include <nspr.h>
36
 
#include <prio.h>
37
 
#include <prerror.h>
38
 
#include <prerr.h>
39
 
#include <prthread.h>
40
 
 
41
 
#include <glib/gi18n-lib.h>
42
 
 
43
 
#ifdef G_OS_WIN32
44
 
#include <winsock2.h>
45
 
#include <ws2tcpip.h>
46
 
#endif
47
 
 
48
 
#include "camel-file-utils.h"
49
 
#include "camel-net-utils.h"
50
 
#include "camel-operation.h"
51
 
#include "camel-service.h"
52
 
#include "camel-tcp-stream-raw.h"
53
 
 
54
 
#define d(x)
55
 
 
56
 
#define IO_TIMEOUT (PR_TicksPerSecond() * 4 * 60)
57
 
#define CONNECT_TIMEOUT (PR_TicksPerSecond () * 1 * 60)
58
 
 
59
 
typedef struct _CamelTcpStreamRawPrivate {
60
 
        PRFileDesc *sockfd;
61
 
} CamelTcpStreamRawPrivate;
62
 
 
63
 
G_DEFINE_TYPE (CamelTcpStreamRaw, camel_tcp_stream_raw, CAMEL_TYPE_TCP_STREAM)
64
 
 
65
 
#ifdef SIMULATE_FLAKY_NETWORK
66
 
static gssize
67
 
flaky_tcp_write (gint fd,
68
 
                 const gchar *buffer,
69
 
                 gsize buflen)
70
 
{
71
 
        gsize len = buflen;
72
 
        gssize nwritten;
73
 
        gint val;
74
 
 
75
 
        if (buflen == 0)
76
 
                return 0;
77
 
 
78
 
        val = 1 + (gint) (10.0 * rand () / (RAND_MAX + 1.0));
79
 
 
80
 
        switch (val) {
81
 
        case 1:
82
 
                printf ("flaky_tcp_write (%d, ..., %d): (-1) EINTR\n", fd, buflen);
83
 
                errno = EINTR;
84
 
                return -1;
85
 
        case 2:
86
 
                printf ("flaky_tcp_write (%d, ..., %d): (-1) EAGAIN\n", fd, buflen);
87
 
                errno = EAGAIN;
88
 
                return -1;
89
 
        case 3:
90
 
                printf ("flaky_tcp_write (%d, ..., %d): (-1) EWOULDBLOCK\n", fd, buflen);
91
 
                errno = EWOULDBLOCK;
92
 
                return -1;
93
 
        case 4:
94
 
        case 5:
95
 
        case 6:
96
 
                len = 1 + (gsize) (buflen * rand () / (RAND_MAX + 1.0));
97
 
                len = MIN (len, buflen);
98
 
                /* fall through... */
99
 
        default:
100
 
                printf ("flaky_tcp_write (%d, ..., %d): (%d) '%.*s'", fd, buflen, len, (gint) len, buffer);
101
 
                nwritten = write (fd, buffer, len);
102
 
                if (nwritten < 0)
103
 
                        printf (" errno => %s\n", g_strerror (errno));
104
 
                else if (nwritten < len)
105
 
                        printf (" only wrote %d bytes\n", nwritten);
106
 
                else
107
 
                        printf ("\n");
108
 
 
109
 
                return nwritten;
110
 
        }
111
 
}
112
 
 
113
 
#define write(fd, buffer, buflen) flaky_tcp_write (fd, buffer, buflen)
114
 
 
115
 
static gssize
116
 
flaky_tcp_read (gint fd,
117
 
                gchar *buffer,
118
 
                gsize buflen)
119
 
{
120
 
        gsize len = buflen;
121
 
        gssize nread;
122
 
        gint val;
123
 
 
124
 
        if (buflen == 0)
125
 
                return 0;
126
 
 
127
 
        val = 1 + (gint) (10.0 * rand () / (RAND_MAX + 1.0));
128
 
 
129
 
        switch (val) {
130
 
        case 1:
131
 
                printf ("flaky_tcp_read (%d, ..., %d): (-1) EINTR\n", fd, buflen);
132
 
                errno = EINTR;
133
 
                return -1;
134
 
        case 2:
135
 
                printf ("flaky_tcp_read (%d, ..., %d): (-1) EAGAIN\n", fd, buflen);
136
 
                errno = EAGAIN;
137
 
                return -1;
138
 
        case 3:
139
 
                printf ("flaky_tcp_read (%d, ..., %d): (-1) EWOULDBLOCK\n", fd, buflen);
140
 
                errno = EWOULDBLOCK;
141
 
                return -1;
142
 
        case 4:
143
 
        case 5:
144
 
        case 6:
145
 
        case 7:
146
 
        case 8:
147
 
        case 9:
148
 
        case 10:
149
 
                len = 1 + (gsize) (10.0 * rand () / (RAND_MAX + 1.0));
150
 
                len = MIN (len, buflen);
151
 
                /* fall through... */
152
 
        default:
153
 
                printf ("flaky_tcp_read (%d, ..., %d): (%d)", fd, buflen, len);
154
 
                nread = read (fd, buffer, len);
155
 
                if (nread < 0)
156
 
                        printf (" errno => %s\n", g_strerror (errno));
157
 
                else if (nread < len)
158
 
                        printf (" only read %d bytes\n", nread);
159
 
                else
160
 
                        printf ("\n");
161
 
 
162
 
                return nread;
163
 
        }
164
 
}
165
 
 
166
 
#define read(fd, buffer, buflen) flaky_tcp_read (fd, buffer, buflen)
167
 
 
168
 
#endif /* SIMULATE_FLAKY_NETWORK */
169
 
 
170
 
static void
171
 
tcp_stream_cancelled (GCancellable *cancellable,
172
 
                      PRThread *thread)
173
 
{
174
 
        PR_Interrupt (thread);
175
 
}
176
 
 
177
 
static void
178
 
tcp_stream_raw_finalize (GObject *object)
179
 
{
180
 
        CamelTcpStreamRaw *stream = CAMEL_TCP_STREAM_RAW (object);
181
 
        CamelTcpStreamRawPrivate *priv = stream->priv;
182
 
 
183
 
        if (priv->sockfd != NULL) {
184
 
                PR_Shutdown (priv->sockfd, PR_SHUTDOWN_BOTH);
185
 
                PR_Close (priv->sockfd);
186
 
        }
187
 
 
188
 
        /* Chain up to parent's finalize() method. */
189
 
        G_OBJECT_CLASS (camel_tcp_stream_raw_parent_class)->finalize (object);
190
 
}
191
 
 
192
 
void
193
 
_set_errno_from_pr_error (gint pr_code)
194
 
{
195
 
        /* FIXME: this should handle more. */
196
 
        switch (pr_code) {
197
 
        case PR_INVALID_ARGUMENT_ERROR:
198
 
                errno = EINVAL;
199
 
                break;
200
 
        case PR_PENDING_INTERRUPT_ERROR:
201
 
                errno = EINTR;
202
 
                break;
203
 
        case PR_IO_PENDING_ERROR:
204
 
                errno = EAGAIN;
205
 
                break;
206
 
#ifdef EWOULDBLOCK
207
 
        case PR_WOULD_BLOCK_ERROR:
208
 
                errno = EWOULDBLOCK;
209
 
                break;
210
 
#endif
211
 
#ifdef EINPROGRESS
212
 
        case PR_IN_PROGRESS_ERROR:
213
 
                errno = EINPROGRESS;
214
 
                break;
215
 
#endif
216
 
#ifdef EALREADY
217
 
        case PR_ALREADY_INITIATED_ERROR:
218
 
                errno = EALREADY;
219
 
                break;
220
 
#endif
221
 
#ifdef EHOSTUNREACH
222
 
        case PR_NETWORK_UNREACHABLE_ERROR:
223
 
                errno = EHOSTUNREACH;
224
 
                break;
225
 
#endif
226
 
#ifdef ECONNREFUSED
227
 
        case PR_CONNECT_REFUSED_ERROR:
228
 
                errno = ECONNREFUSED;
229
 
                break;
230
 
#endif
231
 
#ifdef ETIMEDOUT
232
 
        case PR_CONNECT_TIMEOUT_ERROR:
233
 
        case PR_IO_TIMEOUT_ERROR:
234
 
                errno = ETIMEDOUT;
235
 
                break;
236
 
#endif
237
 
#ifdef ENOTCONN
238
 
        case PR_NOT_CONNECTED_ERROR:
239
 
                errno = ENOTCONN;
240
 
                break;
241
 
#endif
242
 
#ifdef ECONNRESET
243
 
        case PR_CONNECT_RESET_ERROR:
244
 
                errno = ECONNRESET;
245
 
                break;
246
 
#endif
247
 
        case PR_IO_ERROR:
248
 
        default:
249
 
                errno = EIO;
250
 
                break;
251
 
        }
252
 
}
253
 
 
254
 
void
255
 
_set_g_error_from_errno (GError **error,
256
 
                         gboolean eintr_means_cancelled)
257
 
{
258
 
        gint errn = errno;
259
 
 
260
 
        if (error)
261
 
                g_clear_error (error);
262
 
 
263
 
        /* This is stolen from camel_read() / camel_write() */
264
 
        if (eintr_means_cancelled && errn == EINTR)
265
 
                g_set_error (
266
 
                        error, G_IO_ERROR,
267
 
                        G_IO_ERROR_CANCELLED,
268
 
                        _("Cancelled"));
269
 
        else
270
 
                g_set_error (
271
 
                        error, G_IO_ERROR,
272
 
                        g_io_error_from_errno (errn),
273
 
                        "%s", g_strerror (errn));
274
 
}
275
 
 
276
 
void
277
 
_set_error_from_pr_error (GError **error)
278
 
{
279
 
        gchar *error_message = NULL;
280
 
        PRInt32 length;
281
 
 
282
 
        length = PR_GetErrorTextLength ();
283
 
        if (length > 0) {
284
 
                error_message = g_malloc0 (length + 1);
285
 
                PR_GetErrorText (error_message);
286
 
        } else {
287
 
                const gchar *str = PR_ErrorToString (PR_GetError (), PR_LANGUAGE_I_DEFAULT);
288
 
                if (!str || !*str)
289
 
                        str = PR_ErrorToName (PR_GetError ());
290
 
 
291
 
                if (str && *str)
292
 
                        error_message = g_strdup (str);
293
 
                else
294
 
                        g_warning (
295
 
                                "NSPR error code %d has no text",
296
 
                                PR_GetError ());
297
 
        }
298
 
 
299
 
        _set_errno_from_pr_error (PR_GetError ());
300
 
 
301
 
        if (error_message != NULL)
302
 
                g_set_error_literal (
303
 
                        error, G_IO_ERROR,
304
 
                        g_io_error_from_errno (errno),
305
 
                        error_message);
306
 
        else
307
 
                g_set_error (
308
 
                        error, G_IO_ERROR,
309
 
                        g_io_error_from_errno (errno),
310
 
                        _("NSPR error code %d"),
311
 
                        PR_GetError ());
312
 
 
313
 
        g_free (error_message);
314
 
}
315
 
 
316
 
static gssize
317
 
read_from_prfd (PRFileDesc *fd,
318
 
                gchar *buffer,
319
 
                gsize n,
320
 
                GCancellable *cancellable,
321
 
                GError **error)
322
 
{
323
 
        gssize bytes_read;
324
 
        gulong cancel_id = 0;
325
 
 
326
 
        if (G_IS_CANCELLABLE (cancellable))
327
 
                cancel_id = g_cancellable_connect (
328
 
                        cancellable, G_CALLBACK (tcp_stream_cancelled),
329
 
                        PR_GetCurrentThread (), (GDestroyNotify) NULL);
330
 
 
331
 
        do {
332
 
                bytes_read = PR_Read (fd, buffer, n);
333
 
        } while (bytes_read == -1 && PR_GetError () == PR_IO_PENDING_ERROR);
334
 
 
335
 
        if (cancel_id > 0)
336
 
                g_cancellable_disconnect (cancellable, cancel_id);
337
 
 
338
 
        if (g_cancellable_set_error_if_cancelled (cancellable, error))
339
 
                return -1;
340
 
 
341
 
        if (bytes_read == -1) {
342
 
                _set_error_from_pr_error (error);
343
 
                return -1;
344
 
        }
345
 
 
346
 
        return bytes_read;
347
 
}
348
 
 
349
 
static gssize
350
 
tcp_stream_raw_read (CamelStream *stream,
351
 
                     gchar *buffer,
352
 
                     gsize n,
353
 
                     GCancellable *cancellable,
354
 
                     GError **error)
355
 
{
356
 
        CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
357
 
        CamelTcpStreamRawPrivate *priv = raw->priv;
358
 
 
359
 
        return read_from_prfd (priv->sockfd, buffer, n, cancellable, error);
360
 
}
361
 
 
362
 
static gssize
363
 
write_to_prfd (PRFileDesc *fd,
364
 
               const gchar *buffer,
365
 
               gsize size,
366
 
               GCancellable *cancellable,
367
 
               GError **error)
368
 
{
369
 
        gssize bytes_written;
370
 
        gssize total_written = 0;
371
 
        gulong cancel_id = 0;
372
 
 
373
 
        if (G_IS_CANCELLABLE (cancellable))
374
 
                cancel_id = g_cancellable_connect (
375
 
                        cancellable, G_CALLBACK (tcp_stream_cancelled),
376
 
                        PR_GetCurrentThread (), (GDestroyNotify) NULL);
377
 
 
378
 
        do {
379
 
                do {
380
 
                        bytes_written = PR_Send (
381
 
                                fd, buffer + total_written,
382
 
                                size - total_written, 0, IO_TIMEOUT);
383
 
                } while (bytes_written == -1 && PR_GetError () == PR_IO_PENDING_ERROR);
384
 
 
385
 
                if (bytes_written > 0)
386
 
                        total_written += bytes_written;
387
 
        } while (bytes_written != -1 && total_written < size);
388
 
 
389
 
        if (cancel_id > 0)
390
 
                g_cancellable_disconnect (cancellable, cancel_id);
391
 
 
392
 
        if (g_cancellable_set_error_if_cancelled (cancellable, error))
393
 
                return -1;
394
 
 
395
 
        if (bytes_written == -1) {
396
 
                _set_error_from_pr_error (error);
397
 
                return -1;
398
 
        }
399
 
 
400
 
        return total_written;
401
 
}
402
 
 
403
 
static gssize
404
 
tcp_stream_raw_write (CamelStream *stream,
405
 
                      const gchar *buffer,
406
 
                      gsize n,
407
 
                      GCancellable *cancellable,
408
 
                      GError **error)
409
 
{
410
 
        CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
411
 
        CamelTcpStreamRawPrivate *priv = raw->priv;
412
 
 
413
 
        return write_to_prfd (priv->sockfd, buffer, n, cancellable, error);
414
 
}
415
 
 
416
 
static gint
417
 
tcp_stream_raw_flush (CamelStream *stream,
418
 
                      GCancellable *cancellable,
419
 
                      GError **error)
420
 
{
421
 
#if 0
422
 
        CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
423
 
        CamelTcpStreamRawPrivate *priv = raw->priv;
424
 
 
425
 
        return PR_Sync (priv->sockfd);
426
 
#endif
427
 
        return 0;
428
 
}
429
 
 
430
 
static gint
431
 
tcp_stream_raw_close (CamelStream *stream,
432
 
                      GCancellable *cancellable,
433
 
                      GError **error)
434
 
{
435
 
        CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
436
 
        CamelTcpStreamRawPrivate *priv = raw->priv;
437
 
 
438
 
        if (priv->sockfd == NULL)
439
 
                errno = EINVAL;
440
 
        else {
441
 
                gboolean err;
442
 
 
443
 
                PR_Shutdown (priv->sockfd, PR_SHUTDOWN_BOTH);
444
 
 
445
 
                err = (PR_Close (priv->sockfd) == PR_FAILURE);
446
 
                priv->sockfd = NULL;
447
 
 
448
 
                if (err)
449
 
                        _set_errno_from_pr_error (PR_GetError ());
450
 
                else
451
 
                        return 0;
452
 
        }
453
 
 
454
 
        _set_g_error_from_errno (error, FALSE);
455
 
        return -1;
456
 
}
457
 
 
458
 
static gint
459
 
sockaddr_to_praddr (struct sockaddr *s,
460
 
                    gint len,
461
 
                    PRNetAddr *addr)
462
 
{
463
 
        /* We assume the ip addresses are the same size - they have to be anyway.
464
 
         * We could probably just use memcpy *shrug* */
465
 
 
466
 
        memset (addr, 0, sizeof (*addr));
467
 
 
468
 
        if (s->sa_family == AF_INET) {
469
 
                struct sockaddr_in *sin = (struct sockaddr_in *) s;
470
 
 
471
 
                if (len < sizeof (*sin))
472
 
                        return -1;
473
 
 
474
 
                addr->inet.family = PR_AF_INET;
475
 
                addr->inet.port = sin->sin_port;
476
 
                memcpy (&addr->inet.ip, &sin->sin_addr, sizeof (addr->inet.ip));
477
 
 
478
 
                return 0;
479
 
        }
480
 
#ifdef ENABLE_IPv6
481
 
        else if (s->sa_family == PR_AF_INET6) {
482
 
                struct sockaddr_in6 *sin = (struct sockaddr_in6 *) s;
483
 
 
484
 
                if (len < sizeof (*sin))
485
 
                        return -1;
486
 
 
487
 
                addr->ipv6.family = PR_AF_INET6;
488
 
                addr->ipv6.port = sin->sin6_port;
489
 
                addr->ipv6.flowinfo = sin->sin6_flowinfo;
490
 
                memcpy (&addr->ipv6.ip, &sin->sin6_addr, sizeof (addr->ipv6.ip));
491
 
                addr->ipv6.scope_id = sin->sin6_scope_id;
492
 
 
493
 
                return 0;
494
 
        }
495
 
#endif
496
 
 
497
 
        return -1;
498
 
}
499
 
 
500
 
static PRFileDesc *
501
 
socket_connect (struct addrinfo *host,
502
 
                GCancellable *cancellable,
503
 
                GError **error)
504
 
{
505
 
        PRNetAddr netaddr;
506
 
        PRFileDesc *fd;
507
 
        PRStatus status;
508
 
        gulong cancel_id = 0;
509
 
 
510
 
        if (sockaddr_to_praddr (host->ai_addr, host->ai_addrlen, &netaddr) != 0) {
511
 
                errno = EINVAL;
512
 
                _set_g_error_from_errno (error, FALSE);
513
 
                return NULL;
514
 
        }
515
 
 
516
 
        fd = PR_OpenTCPSocket (netaddr.raw.family);
517
 
        if (fd == NULL) {
518
 
                _set_errno_from_pr_error (PR_GetError ());
519
 
                _set_g_error_from_errno (error, FALSE);
520
 
                return NULL;
521
 
        }
522
 
 
523
 
        if (G_IS_CANCELLABLE (cancellable))
524
 
                cancel_id = g_cancellable_connect (
525
 
                        cancellable, G_CALLBACK (tcp_stream_cancelled),
526
 
                        PR_GetCurrentThread (), (GDestroyNotify) NULL);
527
 
 
528
 
        status = PR_Connect (fd, &netaddr, CONNECT_TIMEOUT);
529
 
 
530
 
        if (cancel_id > 0)
531
 
                g_cancellable_disconnect (cancellable, cancel_id);
532
 
 
533
 
        if (g_cancellable_set_error_if_cancelled (cancellable, error))
534
 
                goto fail;
535
 
 
536
 
        if (status == PR_FAILURE) {
537
 
                _set_error_from_pr_error (error);
538
 
                goto fail;
539
 
        }
540
 
 
541
 
        return fd;
542
 
 
543
 
fail:
544
 
        PR_Shutdown (fd, PR_SHUTDOWN_BOTH);
545
 
        PR_Close (fd);
546
 
 
547
 
        return NULL;
548
 
}
549
 
 
550
 
/* Just opens a TCP socket to a (presumed) SOCKS proxy.  Does not actually
551
 
 * negotiate anything with the proxy; this is just to create the socket and connect.
552
 
 */
553
 
static PRFileDesc *
554
 
connect_to_proxy (CamelTcpStreamRaw *raw,
555
 
                  const gchar *proxy_host,
556
 
                  gint proxy_port,
557
 
                  GCancellable *cancellable,
558
 
                  GError **error)
559
 
{
560
 
        struct addrinfo *addr, *ai, hints;
561
 
        gchar serv[16];
562
 
        PRFileDesc *fd;
563
 
        gint save_errno;
564
 
 
565
 
        g_assert (proxy_host != NULL);
566
 
 
567
 
        d (g_print ("TcpStreamRaw %p: connecting to proxy %s:%d {\n  resolving proxy host\n", raw, proxy_host, proxy_port));
568
 
 
569
 
        sprintf (serv, "%d", proxy_port);
570
 
 
571
 
        memset (&hints, 0, sizeof (hints));
572
 
        hints.ai_socktype = SOCK_STREAM;
573
 
 
574
 
        addr = camel_getaddrinfo (
575
 
                proxy_host, serv, &hints, cancellable, error);
576
 
        if (!addr)
577
 
                return NULL;
578
 
 
579
 
        d (g_print ("  creating socket and connecting\n"));
580
 
 
581
 
        ai = addr;
582
 
        while (ai) {
583
 
                fd = socket_connect (ai, cancellable, error);
584
 
                if (fd)
585
 
                        goto out;
586
 
 
587
 
                ai = ai->ai_next;
588
 
        }
589
 
 
590
 
out:
591
 
        save_errno = errno;
592
 
 
593
 
        camel_freeaddrinfo (addr);
594
 
 
595
 
        if (!fd) {
596
 
                errno = save_errno;
597
 
                d (g_print ("  could not connect: errno %d\n", errno));
598
 
        }
599
 
 
600
 
        return fd;
601
 
}
602
 
 
603
 
/* Returns the FD of a socket, already connected to and validated by the SOCKS4
604
 
 * proxy that is configured in the stream.  Otherwise returns NULL.  Assumes that
605
 
 * a proxy *is* configured with camel_tcp_stream_set_socks_proxy().  Only tries the first
606
 
 * connect_addr; if you want to traverse all the addrinfos, call this function for each of them.
607
 
 */
608
 
static PRFileDesc *
609
 
connect_to_socks4_proxy (CamelTcpStreamRaw *raw,
610
 
                         const gchar *proxy_host,
611
 
                         gint proxy_port,
612
 
                         struct addrinfo *connect_addr,
613
 
                         GCancellable *cancellable,
614
 
                         GError **error)
615
 
{
616
 
        PRFileDesc *fd;
617
 
        gchar request[9];
618
 
        struct sockaddr_in *sin;
619
 
        gchar reply[8]; /* note that replies are 8 bytes, even if only the first 2 are used */
620
 
        gint save_errno;
621
 
 
622
 
        g_assert (connect_addr->ai_addr->sa_family == AF_INET);
623
 
 
624
 
        fd = connect_to_proxy (
625
 
                raw, proxy_host, proxy_port, cancellable, error);
626
 
        if (!fd)
627
 
                goto error;
628
 
 
629
 
        sin = (struct sockaddr_in *) connect_addr->ai_addr;
630
 
 
631
 
        request[0] = 0x04;                              /* SOCKS4 */
632
 
        request[1] = 0x01;                              /* CONNECT */
633
 
        memcpy (request + 2, &sin->sin_port, 2);        /* port in network byte order */
634
 
        memcpy (request + 4, &sin->sin_addr.s_addr, 4); /* address in network byte order */
635
 
        request[8] = 0x00;                              /* terminator */
636
 
 
637
 
        d (g_print ("  writing SOCKS4 request to connect to actual host\n"));
638
 
        if (write_to_prfd (fd, request, sizeof (request), cancellable, error) != sizeof (request)) {
639
 
                d (g_print ("  failed: %d\n", errno));
640
 
                goto error;
641
 
        }
642
 
 
643
 
        d (g_print ("  reading SOCKS4 reply\n"));
644
 
        if (read_from_prfd (fd, reply, sizeof (reply), cancellable, error) != sizeof (reply)) {
645
 
                d (g_print ("  failed: %d\n", errno));
646
 
                g_set_error (
647
 
                        error, CAMEL_PROXY_ERROR,
648
 
                        CAMEL_PROXY_ERROR_PROXY_NOT_SUPPORTED,
649
 
                        _("The proxy host does not support SOCKS4"));
650
 
                goto error;
651
 
        }
652
 
 
653
 
        if (reply[0] != 0) { /* version of reply code is 0 */
654
 
#ifdef G_OS_WIN32
655
 
                errno = WSAECONNREFUSED;
656
 
#else
657
 
                errno = ECONNREFUSED;
658
 
#endif
659
 
                g_set_error (
660
 
                        error, CAMEL_PROXY_ERROR,
661
 
                        CAMEL_PROXY_ERROR_PROXY_NOT_SUPPORTED,
662
 
                        _("The proxy host does not support SOCKS4"));
663
 
                goto error;
664
 
        }
665
 
 
666
 
        if (reply[1] != 90) {   /* 90 means "request granted" */
667
 
#ifdef G_OS_WIN32
668
 
                errno = WSAECONNREFUSED;
669
 
#else
670
 
                errno = ECONNREFUSED;
671
 
#endif
672
 
                g_set_error (
673
 
                        error, CAMEL_PROXY_ERROR,
674
 
                        CAMEL_PROXY_ERROR_CANT_AUTHENTICATE,
675
 
                        _("The proxy host denied our request: code %d"),
676
 
                        reply[1]);
677
 
                goto error;
678
 
        }
679
 
 
680
 
        /* We are now proxied; we are ready to send "normal" data through the socket */
681
 
 
682
 
        d (g_print ("  success\n"));
683
 
 
684
 
        goto out;
685
 
 
686
 
error:
687
 
        if (fd) {
688
 
                save_errno = errno;
689
 
                PR_Shutdown (fd, PR_SHUTDOWN_BOTH);
690
 
                PR_Close (fd);
691
 
                errno = save_errno;
692
 
                fd = NULL;
693
 
        }
694
 
 
695
 
        d (g_print ("  returning errno %d\n", errno));
696
 
 
697
 
out:
698
 
 
699
 
        return fd;
700
 
}
701
 
 
702
 
/* Resolves a port number using getaddrinfo().  Returns 0 if the port can't be resolved or if the operation is cancelled */
703
 
static gint
704
 
resolve_port (const gchar *service,
705
 
              gint fallback_port,
706
 
              GCancellable *cancellable,
707
 
              GError **error)
708
 
{
709
 
        struct addrinfo *ai;
710
 
        GError *my_error;
711
 
        gint port;
712
 
 
713
 
        port = 0;
714
 
 
715
 
        my_error = NULL;
716
 
        /* FIXME: camel_getaddrinfo() does not take NULL hostnames.  This is different
717
 
         * from the standard getaddrinfo(), which lets you pass a NULL hostname
718
 
         * if you just want to resolve a port number.
719
 
         */
720
 
        ai = camel_getaddrinfo (
721
 
                "localhost", service, NULL, cancellable, &my_error);
722
 
        if (ai == NULL && fallback_port != 0 && !g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
723
 
                port = fallback_port;
724
 
        else if (ai == NULL) {
725
 
                g_propagate_error (error, my_error);
726
 
        } else if (ai) {
727
 
                if (ai->ai_family == AF_INET) {
728
 
                        port = ((struct sockaddr_in *) ai->ai_addr)->sin_port;
729
 
                }
730
 
#ifdef ENABLE_IPv6
731
 
                else if (ai->ai_family == AF_INET6) {
732
 
                        port = ((struct sockaddr_in6 *) ai->ai_addr)->sin6_port;
733
 
                }
734
 
#endif
735
 
                else {
736
 
                        g_assert_not_reached ();
737
 
                }
738
 
 
739
 
                camel_freeaddrinfo (ai);
740
 
 
741
 
                port = g_ntohs (port);
742
 
        }
743
 
 
744
 
        return port;
745
 
}
746
 
 
747
 
static gboolean
748
 
socks5_initiate_and_request_authentication (CamelTcpStreamRaw *raw,
749
 
                                            PRFileDesc *fd,
750
 
                                            GCancellable *cancellable,
751
 
                                            GError **error)
752
 
{
753
 
        gchar request[3];
754
 
        gchar reply[2];
755
 
 
756
 
        request[0] = 0x05;      /* SOCKS5 */
757
 
        request[1] = 1;         /* Number of authentication methods.  We just support "unauthenticated" for now. */
758
 
        request[2] = 0;         /* no authentication, please - extending this is left as an exercise for the reader */
759
 
 
760
 
        d (g_print ("  writing SOCKS5 request for authentication\n"));
761
 
        if (write_to_prfd (fd, request, sizeof (request), cancellable, error) != sizeof (request)) {
762
 
                d (g_print ("  failed: %d\n", errno));
763
 
                return FALSE;
764
 
        }
765
 
 
766
 
        d (g_print ("  reading SOCKS5 reply\n"));
767
 
        if (read_from_prfd (fd, reply, sizeof (reply), cancellable, error) != sizeof (reply)) {
768
 
                d (g_print ("  failed: %d\n", errno));
769
 
                g_clear_error (error);
770
 
                g_set_error (
771
 
                        error, CAMEL_PROXY_ERROR,
772
 
                        CAMEL_PROXY_ERROR_PROXY_NOT_SUPPORTED,
773
 
                        _("The proxy host does not support SOCKS5"));
774
 
                return FALSE;
775
 
        }
776
 
 
777
 
        if (reply[0] != 5) {            /* server supports SOCKS5 */
778
 
                g_set_error (
779
 
                        error, CAMEL_PROXY_ERROR,
780
 
                        CAMEL_PROXY_ERROR_PROXY_NOT_SUPPORTED,
781
 
                        _("The proxy host does not support SOCKS5"));
782
 
                return FALSE;
783
 
        }
784
 
 
785
 
        if (reply[1] != 0) {            /* and it grants us no authentication (see request[2]) */
786
 
                g_set_error (
787
 
                        error, CAMEL_PROXY_ERROR,
788
 
                        CAMEL_PROXY_ERROR_CANT_AUTHENTICATE,
789
 
                        _("Could not find a suitable authentication type: code 0x%x"),
790
 
                        reply[1]);
791
 
                return FALSE;
792
 
        }
793
 
 
794
 
        return TRUE;
795
 
}
796
 
 
797
 
static const gchar *
798
 
socks5_reply_error_to_string (gchar error_code)
799
 
{
800
 
        switch (error_code) {
801
 
        case 0x01: return _("General SOCKS server failure");
802
 
        case 0x02: return _("SOCKS server's rules do not allow connection");
803
 
        case 0x03: return _("Network is unreachable from SOCKS server");
804
 
        case 0x04: return _("Host is unreachable from SOCKS server");
805
 
        case 0x05: return _("Connection refused");
806
 
        case 0x06: return _("Time-to-live expired");
807
 
        case 0x07: return _("Command not supported by SOCKS server");
808
 
        case 0x08: return _("Address type not supported by SOCKS server");
809
 
        default: return _("Unknown error from SOCKS server");
810
 
        }
811
 
}
812
 
 
813
 
static gboolean
814
 
socks5_consume_reply_address (CamelTcpStreamRaw *raw,
815
 
                              PRFileDesc *fd,
816
 
                              GCancellable *cancellable,
817
 
                              GError **error)
818
 
{
819
 
        gchar address_type;
820
 
        gint bytes_to_consume;
821
 
        gchar *address_and_port;
822
 
 
823
 
        address_and_port = NULL;
824
 
 
825
 
        if (read_from_prfd (fd, &address_type, sizeof (address_type), cancellable, error) != sizeof (address_type))
826
 
                goto incomplete_reply;
827
 
 
828
 
        if (address_type == 0x01)
829
 
                bytes_to_consume = 4; /* IPv4 address */
830
 
        else if (address_type == 0x04)
831
 
                bytes_to_consume = 16; /* IPv6 address */
832
 
        else if (address_type == 0x03) {
833
 
                guchar address_len;
834
 
 
835
 
                /* we'll get an octet with the address length, and then the address itself */
836
 
 
837
 
                if (read_from_prfd (fd, (gchar *) &address_len, sizeof (address_len), cancellable, error) != sizeof (address_len))
838
 
                        goto incomplete_reply;
839
 
 
840
 
                bytes_to_consume = address_len;
841
 
        } else {
842
 
                g_set_error (error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_NOT_CONNECTED, _("Got unknown address type from SOCKS server"));
843
 
                return FALSE;
844
 
        }
845
 
 
846
 
        bytes_to_consume += 2; /* 2 octets for port number */
847
 
        address_and_port = g_new (gchar, bytes_to_consume);
848
 
 
849
 
        if (read_from_prfd (fd, address_and_port, bytes_to_consume, cancellable, error) != bytes_to_consume)
850
 
                goto incomplete_reply;
851
 
 
852
 
        g_free (address_and_port); /* Currently we don't do anything to these; maybe some authenticated method will need them later */
853
 
 
854
 
        return TRUE;
855
 
 
856
 
incomplete_reply:
857
 
        g_free (address_and_port);
858
 
 
859
 
        g_clear_error (error);
860
 
        g_set_error (error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_NOT_CONNECTED, _("Incomplete reply from SOCKS server"));
861
 
        return FALSE;
862
 
}
863
 
 
864
 
static gboolean
865
 
socks5_request_connect (CamelTcpStreamRaw *raw,
866
 
                        PRFileDesc *fd,
867
 
                        const gchar *host,
868
 
                        gint port,
869
 
                        GCancellable *cancellable,
870
 
                        GError **error)
871
 
{
872
 
        gchar *request;
873
 
        gchar reply[3];
874
 
        gint host_len;
875
 
        gint request_len;
876
 
        gint num_written;
877
 
 
878
 
        host_len = strlen (host);
879
 
        if (host_len > 255) {
880
 
                g_set_error (error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, _("Hostname is too long (maximum is 255 characters)"));
881
 
                return FALSE;
882
 
        }
883
 
 
884
 
        request_len = 4 + 1 + host_len + 2; /* Request header + octect for host_len + host + 2 octets for port */
885
 
        request = g_new (gchar, request_len);
886
 
 
887
 
        request[0] = 0x05;      /* Version - SOCKS5 */
888
 
        request[1] = 0x01;      /* Command - CONNECT */
889
 
        request[2] = 0x00;      /* Reserved */
890
 
        request[3] = 0x03;      /* ATYP - address type - DOMAINNAME */
891
 
        request[4] = host_len;
892
 
        memcpy (request + 5, host, host_len);
893
 
        request[5 + host_len] = (port & 0xff00) >> 8; /* high byte of port */
894
 
        request[5 + host_len + 1] = port & 0xff;      /* low byte of port */
895
 
 
896
 
        d (g_print ("  writing SOCKS5 request for connection\n"));
897
 
        num_written = write_to_prfd (fd, request, request_len, cancellable, error);
898
 
        g_free (request);
899
 
 
900
 
        if (num_written != request_len) {
901
 
                d (g_print ("  failed: %d\n", errno));
902
 
                return FALSE;
903
 
        }
904
 
 
905
 
        d (g_print ("  reading SOCKS5 reply\n"));
906
 
        if (read_from_prfd (fd, reply, sizeof (reply), cancellable, error) != sizeof (reply)) {
907
 
                d (g_print ("  failed: %d\n", errno));
908
 
                return FALSE;
909
 
        }
910
 
 
911
 
        if (reply[0] != 0x05) { /* SOCKS5 */
912
 
                g_set_error (error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_NOT_CONNECTED, _("Invalid reply from proxy server"));
913
 
                return FALSE;
914
 
        }
915
 
 
916
 
        if (reply[1] != 0x00) { /* error code */
917
 
                g_set_error (error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_NOT_CONNECTED, "%s", socks5_reply_error_to_string (reply[1]));
918
 
                return FALSE;
919
 
        }
920
 
 
921
 
        if (reply[2] != 0x00) { /* reserved - must be 0 */
922
 
                g_set_error (error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_NOT_CONNECTED, _("Invalid reply from proxy server"));
923
 
                return FALSE;
924
 
        }
925
 
 
926
 
        /* The rest of the reply is the address that the SOCKS server uses to
927
 
         * identify to the final host.  This is of variable length, so we must
928
 
         * consume it by hand.
929
 
         */
930
 
        if (!socks5_consume_reply_address (raw, fd, cancellable, error))
931
 
                return FALSE;
932
 
 
933
 
        return TRUE;
934
 
}
935
 
 
936
 
/* RFC 1928 - SOCKS protocol version 5 */
937
 
static PRFileDesc *
938
 
connect_to_socks5_proxy (CamelTcpStreamRaw *raw,
939
 
                         const gchar *proxy_host,
940
 
                         gint proxy_port,
941
 
                         const gchar *host,
942
 
                         const gchar *service,
943
 
                         gint fallback_port,
944
 
                         GCancellable *cancellable,
945
 
                         GError **error)
946
 
{
947
 
        PRFileDesc *fd;
948
 
        gint port;
949
 
 
950
 
        fd = connect_to_proxy (
951
 
                raw, proxy_host, proxy_port, cancellable, error);
952
 
        if (!fd)
953
 
                goto error;
954
 
 
955
 
        port = resolve_port (service, fallback_port, cancellable, error);
956
 
        if (port == 0)
957
 
                goto error;
958
 
 
959
 
        if (!socks5_initiate_and_request_authentication (raw, fd, cancellable, error))
960
 
                goto error;
961
 
 
962
 
        if (!socks5_request_connect (raw, fd, host, port, cancellable, error))
963
 
                goto error;
964
 
 
965
 
        d (g_print ("  success\n"));
966
 
 
967
 
        goto out;
968
 
 
969
 
error:
970
 
        if (fd) {
971
 
                gint save_errno;
972
 
 
973
 
                save_errno = errno;
974
 
                PR_Shutdown (fd, PR_SHUTDOWN_BOTH);
975
 
                PR_Close (fd);
976
 
                errno = save_errno;
977
 
                fd = NULL;
978
 
        }
979
 
 
980
 
        d (g_print ("  returning errno %d\n", errno));
981
 
 
982
 
out:
983
 
 
984
 
        d (g_print ("}\n"));
985
 
 
986
 
        return fd;
987
 
 
988
 
}
989
 
 
990
 
static gint
991
 
tcp_stream_raw_connect (CamelTcpStream *stream,
992
 
                        const gchar *host,
993
 
                        const gchar *service,
994
 
                        gint fallback_port,
995
 
                        GCancellable *cancellable,
996
 
                        GError **error)
997
 
{
998
 
        CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
999
 
        CamelTcpStreamRawPrivate *priv = raw->priv;
1000
 
        struct addrinfo *addr, *ai;
1001
 
        struct addrinfo hints;
1002
 
        GError *my_error;
1003
 
        gint retval;
1004
 
        const gchar *proxy_host;
1005
 
        gint proxy_port;
1006
 
 
1007
 
        camel_tcp_stream_peek_socks_proxy (stream, &proxy_host, &proxy_port);
1008
 
 
1009
 
        if (proxy_host) {
1010
 
                /* First, try SOCKS5, which does name resolution itself */
1011
 
 
1012
 
                my_error = NULL;
1013
 
                priv->sockfd = connect_to_socks5_proxy (
1014
 
                        raw, proxy_host, proxy_port, host, service,
1015
 
                        fallback_port, cancellable, &my_error);
1016
 
                if (priv->sockfd)
1017
 
                        return 0;
1018
 
                else if (g_error_matches (my_error, CAMEL_PROXY_ERROR, CAMEL_PROXY_ERROR_CANT_AUTHENTICATE)
1019
 
                         || !g_error_matches (my_error, CAMEL_PROXY_ERROR, CAMEL_PROXY_ERROR_PROXY_NOT_SUPPORTED)) {
1020
 
                        g_propagate_error (error, my_error);
1021
 
                        return -1;
1022
 
                }
1023
 
        }
1024
 
 
1025
 
        /* Second, do name resolution ourselves and try SOCKS4 or a normal connection */
1026
 
 
1027
 
        memset (&hints, 0, sizeof (hints));
1028
 
        hints.ai_socktype = SOCK_STREAM;
1029
 
        hints.ai_family = PF_UNSPEC;
1030
 
 
1031
 
        my_error = NULL;
1032
 
        addr = camel_getaddrinfo (
1033
 
                host, service, &hints, cancellable, &my_error);
1034
 
        if (addr == NULL && fallback_port != 0 && !g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
1035
 
                gchar str_port[16];
1036
 
 
1037
 
                g_clear_error (&my_error);
1038
 
                sprintf (str_port, "%d", fallback_port);
1039
 
                addr = camel_getaddrinfo (
1040
 
                        host, str_port, &hints, cancellable, &my_error);
1041
 
        }
1042
 
 
1043
 
        if (addr == NULL) {
1044
 
                g_propagate_error (error, my_error);
1045
 
                return -1;
1046
 
        }
1047
 
 
1048
 
        ai = addr;
1049
 
 
1050
 
        while (ai) {
1051
 
                if (proxy_host) {
1052
 
                        /* SOCKS4 only does IPv4 */
1053
 
                        if (ai->ai_addr->sa_family == AF_INET)
1054
 
                                priv->sockfd = connect_to_socks4_proxy (
1055
 
                                        raw, proxy_host, proxy_port,
1056
 
                                        ai, cancellable, error);
1057
 
                } else
1058
 
                        priv->sockfd = socket_connect (ai, cancellable, error);
1059
 
 
1060
 
                if (priv->sockfd) {
1061
 
                        retval = 0;
1062
 
                        goto out;
1063
 
                }
1064
 
 
1065
 
                if (ai->ai_next != NULL)
1066
 
                        g_clear_error (error); /* Only preserve the error from the last try, in case no tries are successful */
1067
 
 
1068
 
                ai = ai->ai_next;
1069
 
        }
1070
 
 
1071
 
        retval = -1;
1072
 
 
1073
 
out:
1074
 
 
1075
 
        camel_freeaddrinfo (addr);
1076
 
 
1077
 
        return retval;
1078
 
}
1079
 
 
1080
 
static gint
1081
 
tcp_stream_raw_getsockopt (CamelTcpStream *stream,
1082
 
                           CamelSockOptData *data)
1083
 
{
1084
 
        CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
1085
 
        CamelTcpStreamRawPrivate *priv = raw->priv;
1086
 
        PRSocketOptionData sodata;
1087
 
 
1088
 
        memset ((gpointer) &sodata, 0, sizeof (sodata));
1089
 
        memcpy ((gpointer) &sodata, (gpointer) data, sizeof (CamelSockOptData));
1090
 
 
1091
 
        if (PR_GetSocketOption (priv->sockfd, &sodata) == PR_FAILURE)
1092
 
                return -1;
1093
 
 
1094
 
        memcpy ((gpointer) data, (gpointer) &sodata, sizeof (CamelSockOptData));
1095
 
 
1096
 
        return 0;
1097
 
}
1098
 
 
1099
 
static gint
1100
 
tcp_stream_raw_setsockopt (CamelTcpStream *stream,
1101
 
                           const CamelSockOptData *data)
1102
 
{
1103
 
        CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
1104
 
        CamelTcpStreamRawPrivate *priv = raw->priv;
1105
 
        PRSocketOptionData sodata;
1106
 
 
1107
 
        memset ((gpointer) &sodata, 0, sizeof (sodata));
1108
 
        memcpy ((gpointer) &sodata, (gpointer) data, sizeof (CamelSockOptData));
1109
 
 
1110
 
        if (PR_SetSocketOption (priv->sockfd, &sodata) == PR_FAILURE)
1111
 
                return -1;
1112
 
 
1113
 
        return 0;
1114
 
}
1115
 
 
1116
 
static struct sockaddr *
1117
 
sockaddr_from_praddr (PRNetAddr *addr,
1118
 
                      socklen_t *len)
1119
 
{
1120
 
        /* We assume the ip addresses are the same size - they have to be anyway */
1121
 
 
1122
 
        if (addr->raw.family == PR_AF_INET) {
1123
 
                struct sockaddr_in *sin = g_malloc0 (sizeof (*sin));
1124
 
 
1125
 
                sin->sin_family = AF_INET;
1126
 
                sin->sin_port = addr->inet.port;
1127
 
                memcpy (&sin->sin_addr, &addr->inet.ip, sizeof (sin->sin_addr));
1128
 
                *len = sizeof(*sin);
1129
 
 
1130
 
                return (struct sockaddr *) sin;
1131
 
        }
1132
 
#ifdef ENABLE_IPv6
1133
 
        else if (addr->raw.family == PR_AF_INET6) {
1134
 
                struct sockaddr_in6 *sin = g_malloc0 (sizeof (*sin));
1135
 
 
1136
 
                sin->sin6_family = AF_INET6;
1137
 
                sin->sin6_port = addr->ipv6.port;
1138
 
                sin->sin6_flowinfo = addr->ipv6.flowinfo;
1139
 
                memcpy (&sin->sin6_addr, &addr->ipv6.ip, sizeof (sin->sin6_addr));
1140
 
                sin->sin6_scope_id = addr->ipv6.scope_id;
1141
 
                *len = sizeof(*sin);
1142
 
 
1143
 
                return (struct sockaddr *) sin;
1144
 
        }
1145
 
#endif
1146
 
 
1147
 
        return NULL;
1148
 
}
1149
 
 
1150
 
static struct sockaddr *
1151
 
tcp_stream_raw_get_local_address (CamelTcpStream *stream,
1152
 
                                  socklen_t *len)
1153
 
{
1154
 
        CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
1155
 
        CamelTcpStreamRawPrivate *priv = raw->priv;
1156
 
        PRNetAddr addr;
1157
 
 
1158
 
        if (PR_GetSockName (priv->sockfd, &addr) != PR_SUCCESS)
1159
 
                return NULL;
1160
 
 
1161
 
        return sockaddr_from_praddr (&addr, len);
1162
 
}
1163
 
 
1164
 
static struct sockaddr *
1165
 
tcp_stream_raw_get_remote_address (CamelTcpStream *stream,
1166
 
                                   socklen_t *len)
1167
 
{
1168
 
        CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
1169
 
        CamelTcpStreamRawPrivate *priv = raw->priv;
1170
 
        PRNetAddr addr;
1171
 
 
1172
 
        if (PR_GetPeerName (priv->sockfd, &addr) != PR_SUCCESS)
1173
 
                return NULL;
1174
 
 
1175
 
        return sockaddr_from_praddr (&addr, len);
1176
 
}
1177
 
 
1178
 
static PRFileDesc *
1179
 
tcp_stream_raw_get_file_desc (CamelTcpStream *stream)
1180
 
{
1181
 
        CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
1182
 
        CamelTcpStreamRawPrivate *priv = raw->priv;
1183
 
 
1184
 
        return priv->sockfd;
1185
 
}
1186
 
 
1187
 
void
1188
 
_camel_tcp_stream_raw_replace_file_desc (CamelTcpStreamRaw *raw,
1189
 
                                         PRFileDesc *new_file_desc)
1190
 
{
1191
 
        CamelTcpStreamRawPrivate *priv = raw->priv;
1192
 
 
1193
 
        priv->sockfd = new_file_desc;
1194
 
}
1195
 
 
1196
 
#define CAMEL_TCP_STREAM_RAW_GET_PRIVATE(obj) \
1197
 
        (G_TYPE_INSTANCE_GET_PRIVATE \
1198
 
        ((obj), CAMEL_TYPE_TCP_STREAM_RAW, CamelTcpStreamRawPrivate))
1199
 
 
1200
 
static void
1201
 
camel_tcp_stream_raw_class_init (CamelTcpStreamRawClass *class)
1202
 
{
1203
 
        GObjectClass *object_class;
1204
 
        CamelStreamClass *stream_class;
1205
 
        CamelTcpStreamClass *tcp_stream_class;
1206
 
 
1207
 
        g_type_class_add_private (class, sizeof (CamelTcpStreamRawPrivate));
1208
 
 
1209
 
        object_class = G_OBJECT_CLASS (class);
1210
 
        object_class->finalize = tcp_stream_raw_finalize;
1211
 
 
1212
 
        stream_class = CAMEL_STREAM_CLASS (class);
1213
 
        stream_class->read = tcp_stream_raw_read;
1214
 
        stream_class->write = tcp_stream_raw_write;
1215
 
        stream_class->flush = tcp_stream_raw_flush;
1216
 
        stream_class->close = tcp_stream_raw_close;
1217
 
 
1218
 
        tcp_stream_class = CAMEL_TCP_STREAM_CLASS (class);
1219
 
        tcp_stream_class->connect = tcp_stream_raw_connect;
1220
 
        tcp_stream_class->getsockopt = tcp_stream_raw_getsockopt;
1221
 
        tcp_stream_class->setsockopt = tcp_stream_raw_setsockopt;
1222
 
        tcp_stream_class->get_local_address = tcp_stream_raw_get_local_address;
1223
 
        tcp_stream_class->get_remote_address = tcp_stream_raw_get_remote_address;
1224
 
        tcp_stream_class->get_file_desc = tcp_stream_raw_get_file_desc;
1225
 
}
1226
 
 
1227
 
static void
1228
 
camel_tcp_stream_raw_init (CamelTcpStreamRaw *stream)
1229
 
{
1230
 
        stream->priv = CAMEL_TCP_STREAM_RAW_GET_PRIVATE (stream);
1231
 
}
1232
 
 
1233
 
G_DEFINE_QUARK (camel-proxy-error-quark, camel_proxy_error)
1234
 
 
1235
 
/**
1236
 
 * camel_tcp_stream_raw_new:
1237
 
 *
1238
 
 * Create a new #CamelTcpStreamRaw object.
1239
 
 *
1240
 
 * Returns: a new #CamelTcpStream object
1241
 
 **/
1242
 
CamelStream *
1243
 
camel_tcp_stream_raw_new (void)
1244
 
{
1245
 
        return g_object_new (CAMEL_TYPE_TCP_STREAM_RAW, NULL);
1246
 
}