1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3
* Authors: Jeffrey Stedfast <fejj@ximian.com>
5
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
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.
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.
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.
33
#include <sys/types.h>
41
#include <glib/gi18n-lib.h>
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"
56
#define IO_TIMEOUT (PR_TicksPerSecond() * 4 * 60)
57
#define CONNECT_TIMEOUT (PR_TicksPerSecond () * 1 * 60)
59
typedef struct _CamelTcpStreamRawPrivate {
61
} CamelTcpStreamRawPrivate;
63
G_DEFINE_TYPE (CamelTcpStreamRaw, camel_tcp_stream_raw, CAMEL_TYPE_TCP_STREAM)
65
#ifdef SIMULATE_FLAKY_NETWORK
67
flaky_tcp_write (gint fd,
78
val = 1 + (gint) (10.0 * rand () / (RAND_MAX + 1.0));
82
printf ("flaky_tcp_write (%d, ..., %d): (-1) EINTR\n", fd, buflen);
86
printf ("flaky_tcp_write (%d, ..., %d): (-1) EAGAIN\n", fd, buflen);
90
printf ("flaky_tcp_write (%d, ..., %d): (-1) EWOULDBLOCK\n", fd, buflen);
96
len = 1 + (gsize) (buflen * rand () / (RAND_MAX + 1.0));
97
len = MIN (len, buflen);
100
printf ("flaky_tcp_write (%d, ..., %d): (%d) '%.*s'", fd, buflen, len, (gint) len, buffer);
101
nwritten = write (fd, buffer, len);
103
printf (" errno => %s\n", g_strerror (errno));
104
else if (nwritten < len)
105
printf (" only wrote %d bytes\n", nwritten);
113
#define write(fd, buffer, buflen) flaky_tcp_write (fd, buffer, buflen)
116
flaky_tcp_read (gint fd,
127
val = 1 + (gint) (10.0 * rand () / (RAND_MAX + 1.0));
131
printf ("flaky_tcp_read (%d, ..., %d): (-1) EINTR\n", fd, buflen);
135
printf ("flaky_tcp_read (%d, ..., %d): (-1) EAGAIN\n", fd, buflen);
139
printf ("flaky_tcp_read (%d, ..., %d): (-1) EWOULDBLOCK\n", fd, buflen);
149
len = 1 + (gsize) (10.0 * rand () / (RAND_MAX + 1.0));
150
len = MIN (len, buflen);
151
/* fall through... */
153
printf ("flaky_tcp_read (%d, ..., %d): (%d)", fd, buflen, len);
154
nread = read (fd, buffer, len);
156
printf (" errno => %s\n", g_strerror (errno));
157
else if (nread < len)
158
printf (" only read %d bytes\n", nread);
166
#define read(fd, buffer, buflen) flaky_tcp_read (fd, buffer, buflen)
168
#endif /* SIMULATE_FLAKY_NETWORK */
171
tcp_stream_cancelled (GCancellable *cancellable,
174
PR_Interrupt (thread);
178
tcp_stream_raw_finalize (GObject *object)
180
CamelTcpStreamRaw *stream = CAMEL_TCP_STREAM_RAW (object);
181
CamelTcpStreamRawPrivate *priv = stream->priv;
183
if (priv->sockfd != NULL) {
184
PR_Shutdown (priv->sockfd, PR_SHUTDOWN_BOTH);
185
PR_Close (priv->sockfd);
188
/* Chain up to parent's finalize() method. */
189
G_OBJECT_CLASS (camel_tcp_stream_raw_parent_class)->finalize (object);
193
_set_errno_from_pr_error (gint pr_code)
195
/* FIXME: this should handle more. */
197
case PR_INVALID_ARGUMENT_ERROR:
200
case PR_PENDING_INTERRUPT_ERROR:
203
case PR_IO_PENDING_ERROR:
207
case PR_WOULD_BLOCK_ERROR:
212
case PR_IN_PROGRESS_ERROR:
217
case PR_ALREADY_INITIATED_ERROR:
222
case PR_NETWORK_UNREACHABLE_ERROR:
223
errno = EHOSTUNREACH;
227
case PR_CONNECT_REFUSED_ERROR:
228
errno = ECONNREFUSED;
232
case PR_CONNECT_TIMEOUT_ERROR:
233
case PR_IO_TIMEOUT_ERROR:
238
case PR_NOT_CONNECTED_ERROR:
243
case PR_CONNECT_RESET_ERROR:
255
_set_g_error_from_errno (GError **error,
256
gboolean eintr_means_cancelled)
261
g_clear_error (error);
263
/* This is stolen from camel_read() / camel_write() */
264
if (eintr_means_cancelled && errn == EINTR)
267
G_IO_ERROR_CANCELLED,
272
g_io_error_from_errno (errn),
273
"%s", g_strerror (errn));
277
_set_error_from_pr_error (GError **error)
279
gchar *error_message = NULL;
282
length = PR_GetErrorTextLength ();
284
error_message = g_malloc0 (length + 1);
285
PR_GetErrorText (error_message);
287
const gchar *str = PR_ErrorToString (PR_GetError (), PR_LANGUAGE_I_DEFAULT);
289
str = PR_ErrorToName (PR_GetError ());
292
error_message = g_strdup (str);
295
"NSPR error code %d has no text",
299
_set_errno_from_pr_error (PR_GetError ());
301
if (error_message != NULL)
302
g_set_error_literal (
304
g_io_error_from_errno (errno),
309
g_io_error_from_errno (errno),
310
_("NSPR error code %d"),
313
g_free (error_message);
317
read_from_prfd (PRFileDesc *fd,
320
GCancellable *cancellable,
324
gulong cancel_id = 0;
326
if (G_IS_CANCELLABLE (cancellable))
327
cancel_id = g_cancellable_connect (
328
cancellable, G_CALLBACK (tcp_stream_cancelled),
329
PR_GetCurrentThread (), (GDestroyNotify) NULL);
332
bytes_read = PR_Read (fd, buffer, n);
333
} while (bytes_read == -1 && PR_GetError () == PR_IO_PENDING_ERROR);
336
g_cancellable_disconnect (cancellable, cancel_id);
338
if (g_cancellable_set_error_if_cancelled (cancellable, error))
341
if (bytes_read == -1) {
342
_set_error_from_pr_error (error);
350
tcp_stream_raw_read (CamelStream *stream,
353
GCancellable *cancellable,
356
CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
357
CamelTcpStreamRawPrivate *priv = raw->priv;
359
return read_from_prfd (priv->sockfd, buffer, n, cancellable, error);
363
write_to_prfd (PRFileDesc *fd,
366
GCancellable *cancellable,
369
gssize bytes_written;
370
gssize total_written = 0;
371
gulong cancel_id = 0;
373
if (G_IS_CANCELLABLE (cancellable))
374
cancel_id = g_cancellable_connect (
375
cancellable, G_CALLBACK (tcp_stream_cancelled),
376
PR_GetCurrentThread (), (GDestroyNotify) NULL);
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);
385
if (bytes_written > 0)
386
total_written += bytes_written;
387
} while (bytes_written != -1 && total_written < size);
390
g_cancellable_disconnect (cancellable, cancel_id);
392
if (g_cancellable_set_error_if_cancelled (cancellable, error))
395
if (bytes_written == -1) {
396
_set_error_from_pr_error (error);
400
return total_written;
404
tcp_stream_raw_write (CamelStream *stream,
407
GCancellable *cancellable,
410
CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
411
CamelTcpStreamRawPrivate *priv = raw->priv;
413
return write_to_prfd (priv->sockfd, buffer, n, cancellable, error);
417
tcp_stream_raw_flush (CamelStream *stream,
418
GCancellable *cancellable,
422
CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
423
CamelTcpStreamRawPrivate *priv = raw->priv;
425
return PR_Sync (priv->sockfd);
431
tcp_stream_raw_close (CamelStream *stream,
432
GCancellable *cancellable,
435
CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
436
CamelTcpStreamRawPrivate *priv = raw->priv;
438
if (priv->sockfd == NULL)
443
PR_Shutdown (priv->sockfd, PR_SHUTDOWN_BOTH);
445
err = (PR_Close (priv->sockfd) == PR_FAILURE);
449
_set_errno_from_pr_error (PR_GetError ());
454
_set_g_error_from_errno (error, FALSE);
459
sockaddr_to_praddr (struct sockaddr *s,
463
/* We assume the ip addresses are the same size - they have to be anyway.
464
* We could probably just use memcpy *shrug* */
466
memset (addr, 0, sizeof (*addr));
468
if (s->sa_family == AF_INET) {
469
struct sockaddr_in *sin = (struct sockaddr_in *) s;
471
if (len < sizeof (*sin))
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));
481
else if (s->sa_family == PR_AF_INET6) {
482
struct sockaddr_in6 *sin = (struct sockaddr_in6 *) s;
484
if (len < sizeof (*sin))
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;
501
socket_connect (struct addrinfo *host,
502
GCancellable *cancellable,
508
gulong cancel_id = 0;
510
if (sockaddr_to_praddr (host->ai_addr, host->ai_addrlen, &netaddr) != 0) {
512
_set_g_error_from_errno (error, FALSE);
516
fd = PR_OpenTCPSocket (netaddr.raw.family);
518
_set_errno_from_pr_error (PR_GetError ());
519
_set_g_error_from_errno (error, FALSE);
523
if (G_IS_CANCELLABLE (cancellable))
524
cancel_id = g_cancellable_connect (
525
cancellable, G_CALLBACK (tcp_stream_cancelled),
526
PR_GetCurrentThread (), (GDestroyNotify) NULL);
528
status = PR_Connect (fd, &netaddr, CONNECT_TIMEOUT);
531
g_cancellable_disconnect (cancellable, cancel_id);
533
if (g_cancellable_set_error_if_cancelled (cancellable, error))
536
if (status == PR_FAILURE) {
537
_set_error_from_pr_error (error);
544
PR_Shutdown (fd, PR_SHUTDOWN_BOTH);
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.
554
connect_to_proxy (CamelTcpStreamRaw *raw,
555
const gchar *proxy_host,
557
GCancellable *cancellable,
560
struct addrinfo *addr, *ai, hints;
565
g_assert (proxy_host != NULL);
567
d (g_print ("TcpStreamRaw %p: connecting to proxy %s:%d {\n resolving proxy host\n", raw, proxy_host, proxy_port));
569
sprintf (serv, "%d", proxy_port);
571
memset (&hints, 0, sizeof (hints));
572
hints.ai_socktype = SOCK_STREAM;
574
addr = camel_getaddrinfo (
575
proxy_host, serv, &hints, cancellable, error);
579
d (g_print (" creating socket and connecting\n"));
583
fd = socket_connect (ai, cancellable, error);
593
camel_freeaddrinfo (addr);
597
d (g_print (" could not connect: errno %d\n", errno));
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.
609
connect_to_socks4_proxy (CamelTcpStreamRaw *raw,
610
const gchar *proxy_host,
612
struct addrinfo *connect_addr,
613
GCancellable *cancellable,
618
struct sockaddr_in *sin;
619
gchar reply[8]; /* note that replies are 8 bytes, even if only the first 2 are used */
622
g_assert (connect_addr->ai_addr->sa_family == AF_INET);
624
fd = connect_to_proxy (
625
raw, proxy_host, proxy_port, cancellable, error);
629
sin = (struct sockaddr_in *) connect_addr->ai_addr;
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 */
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));
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));
647
error, CAMEL_PROXY_ERROR,
648
CAMEL_PROXY_ERROR_PROXY_NOT_SUPPORTED,
649
_("The proxy host does not support SOCKS4"));
653
if (reply[0] != 0) { /* version of reply code is 0 */
655
errno = WSAECONNREFUSED;
657
errno = ECONNREFUSED;
660
error, CAMEL_PROXY_ERROR,
661
CAMEL_PROXY_ERROR_PROXY_NOT_SUPPORTED,
662
_("The proxy host does not support SOCKS4"));
666
if (reply[1] != 90) { /* 90 means "request granted" */
668
errno = WSAECONNREFUSED;
670
errno = ECONNREFUSED;
673
error, CAMEL_PROXY_ERROR,
674
CAMEL_PROXY_ERROR_CANT_AUTHENTICATE,
675
_("The proxy host denied our request: code %d"),
680
/* We are now proxied; we are ready to send "normal" data through the socket */
682
d (g_print (" success\n"));
689
PR_Shutdown (fd, PR_SHUTDOWN_BOTH);
695
d (g_print (" returning errno %d\n", errno));
702
/* Resolves a port number using getaddrinfo(). Returns 0 if the port can't be resolved or if the operation is cancelled */
704
resolve_port (const gchar *service,
706
GCancellable *cancellable,
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.
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);
727
if (ai->ai_family == AF_INET) {
728
port = ((struct sockaddr_in *) ai->ai_addr)->sin_port;
731
else if (ai->ai_family == AF_INET6) {
732
port = ((struct sockaddr_in6 *) ai->ai_addr)->sin6_port;
736
g_assert_not_reached ();
739
camel_freeaddrinfo (ai);
741
port = g_ntohs (port);
748
socks5_initiate_and_request_authentication (CamelTcpStreamRaw *raw,
750
GCancellable *cancellable,
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 */
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));
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);
771
error, CAMEL_PROXY_ERROR,
772
CAMEL_PROXY_ERROR_PROXY_NOT_SUPPORTED,
773
_("The proxy host does not support SOCKS5"));
777
if (reply[0] != 5) { /* server supports SOCKS5 */
779
error, CAMEL_PROXY_ERROR,
780
CAMEL_PROXY_ERROR_PROXY_NOT_SUPPORTED,
781
_("The proxy host does not support SOCKS5"));
785
if (reply[1] != 0) { /* and it grants us no authentication (see request[2]) */
787
error, CAMEL_PROXY_ERROR,
788
CAMEL_PROXY_ERROR_CANT_AUTHENTICATE,
789
_("Could not find a suitable authentication type: code 0x%x"),
798
socks5_reply_error_to_string (gchar error_code)
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");
814
socks5_consume_reply_address (CamelTcpStreamRaw *raw,
816
GCancellable *cancellable,
820
gint bytes_to_consume;
821
gchar *address_and_port;
823
address_and_port = NULL;
825
if (read_from_prfd (fd, &address_type, sizeof (address_type), cancellable, error) != sizeof (address_type))
826
goto incomplete_reply;
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) {
835
/* we'll get an octet with the address length, and then the address itself */
837
if (read_from_prfd (fd, (gchar *) &address_len, sizeof (address_len), cancellable, error) != sizeof (address_len))
838
goto incomplete_reply;
840
bytes_to_consume = address_len;
842
g_set_error (error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_NOT_CONNECTED, _("Got unknown address type from SOCKS server"));
846
bytes_to_consume += 2; /* 2 octets for port number */
847
address_and_port = g_new (gchar, bytes_to_consume);
849
if (read_from_prfd (fd, address_and_port, bytes_to_consume, cancellable, error) != bytes_to_consume)
850
goto incomplete_reply;
852
g_free (address_and_port); /* Currently we don't do anything to these; maybe some authenticated method will need them later */
857
g_free (address_and_port);
859
g_clear_error (error);
860
g_set_error (error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_NOT_CONNECTED, _("Incomplete reply from SOCKS server"));
865
socks5_request_connect (CamelTcpStreamRaw *raw,
869
GCancellable *cancellable,
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)"));
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);
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 */
896
d (g_print (" writing SOCKS5 request for connection\n"));
897
num_written = write_to_prfd (fd, request, request_len, cancellable, error);
900
if (num_written != request_len) {
901
d (g_print (" failed: %d\n", errno));
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));
911
if (reply[0] != 0x05) { /* SOCKS5 */
912
g_set_error (error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_NOT_CONNECTED, _("Invalid reply from proxy server"));
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]));
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"));
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.
930
if (!socks5_consume_reply_address (raw, fd, cancellable, error))
936
/* RFC 1928 - SOCKS protocol version 5 */
938
connect_to_socks5_proxy (CamelTcpStreamRaw *raw,
939
const gchar *proxy_host,
942
const gchar *service,
944
GCancellable *cancellable,
950
fd = connect_to_proxy (
951
raw, proxy_host, proxy_port, cancellable, error);
955
port = resolve_port (service, fallback_port, cancellable, error);
959
if (!socks5_initiate_and_request_authentication (raw, fd, cancellable, error))
962
if (!socks5_request_connect (raw, fd, host, port, cancellable, error))
965
d (g_print (" success\n"));
974
PR_Shutdown (fd, PR_SHUTDOWN_BOTH);
980
d (g_print (" returning errno %d\n", errno));
991
tcp_stream_raw_connect (CamelTcpStream *stream,
993
const gchar *service,
995
GCancellable *cancellable,
998
CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
999
CamelTcpStreamRawPrivate *priv = raw->priv;
1000
struct addrinfo *addr, *ai;
1001
struct addrinfo hints;
1004
const gchar *proxy_host;
1007
camel_tcp_stream_peek_socks_proxy (stream, &proxy_host, &proxy_port);
1010
/* First, try SOCKS5, which does name resolution itself */
1013
priv->sockfd = connect_to_socks5_proxy (
1014
raw, proxy_host, proxy_port, host, service,
1015
fallback_port, cancellable, &my_error);
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);
1025
/* Second, do name resolution ourselves and try SOCKS4 or a normal connection */
1027
memset (&hints, 0, sizeof (hints));
1028
hints.ai_socktype = SOCK_STREAM;
1029
hints.ai_family = PF_UNSPEC;
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)) {
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);
1044
g_propagate_error (error, my_error);
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);
1058
priv->sockfd = socket_connect (ai, cancellable, error);
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 */
1075
camel_freeaddrinfo (addr);
1081
tcp_stream_raw_getsockopt (CamelTcpStream *stream,
1082
CamelSockOptData *data)
1084
CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
1085
CamelTcpStreamRawPrivate *priv = raw->priv;
1086
PRSocketOptionData sodata;
1088
memset ((gpointer) &sodata, 0, sizeof (sodata));
1089
memcpy ((gpointer) &sodata, (gpointer) data, sizeof (CamelSockOptData));
1091
if (PR_GetSocketOption (priv->sockfd, &sodata) == PR_FAILURE)
1094
memcpy ((gpointer) data, (gpointer) &sodata, sizeof (CamelSockOptData));
1100
tcp_stream_raw_setsockopt (CamelTcpStream *stream,
1101
const CamelSockOptData *data)
1103
CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
1104
CamelTcpStreamRawPrivate *priv = raw->priv;
1105
PRSocketOptionData sodata;
1107
memset ((gpointer) &sodata, 0, sizeof (sodata));
1108
memcpy ((gpointer) &sodata, (gpointer) data, sizeof (CamelSockOptData));
1110
if (PR_SetSocketOption (priv->sockfd, &sodata) == PR_FAILURE)
1116
static struct sockaddr *
1117
sockaddr_from_praddr (PRNetAddr *addr,
1120
/* We assume the ip addresses are the same size - they have to be anyway */
1122
if (addr->raw.family == PR_AF_INET) {
1123
struct sockaddr_in *sin = g_malloc0 (sizeof (*sin));
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);
1130
return (struct sockaddr *) sin;
1133
else if (addr->raw.family == PR_AF_INET6) {
1134
struct sockaddr_in6 *sin = g_malloc0 (sizeof (*sin));
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);
1143
return (struct sockaddr *) sin;
1150
static struct sockaddr *
1151
tcp_stream_raw_get_local_address (CamelTcpStream *stream,
1154
CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
1155
CamelTcpStreamRawPrivate *priv = raw->priv;
1158
if (PR_GetSockName (priv->sockfd, &addr) != PR_SUCCESS)
1161
return sockaddr_from_praddr (&addr, len);
1164
static struct sockaddr *
1165
tcp_stream_raw_get_remote_address (CamelTcpStream *stream,
1168
CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
1169
CamelTcpStreamRawPrivate *priv = raw->priv;
1172
if (PR_GetPeerName (priv->sockfd, &addr) != PR_SUCCESS)
1175
return sockaddr_from_praddr (&addr, len);
1179
tcp_stream_raw_get_file_desc (CamelTcpStream *stream)
1181
CamelTcpStreamRaw *raw = CAMEL_TCP_STREAM_RAW (stream);
1182
CamelTcpStreamRawPrivate *priv = raw->priv;
1184
return priv->sockfd;
1188
_camel_tcp_stream_raw_replace_file_desc (CamelTcpStreamRaw *raw,
1189
PRFileDesc *new_file_desc)
1191
CamelTcpStreamRawPrivate *priv = raw->priv;
1193
priv->sockfd = new_file_desc;
1196
#define CAMEL_TCP_STREAM_RAW_GET_PRIVATE(obj) \
1197
(G_TYPE_INSTANCE_GET_PRIVATE \
1198
((obj), CAMEL_TYPE_TCP_STREAM_RAW, CamelTcpStreamRawPrivate))
1201
camel_tcp_stream_raw_class_init (CamelTcpStreamRawClass *class)
1203
GObjectClass *object_class;
1204
CamelStreamClass *stream_class;
1205
CamelTcpStreamClass *tcp_stream_class;
1207
g_type_class_add_private (class, sizeof (CamelTcpStreamRawPrivate));
1209
object_class = G_OBJECT_CLASS (class);
1210
object_class->finalize = tcp_stream_raw_finalize;
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;
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;
1228
camel_tcp_stream_raw_init (CamelTcpStreamRaw *stream)
1230
stream->priv = CAMEL_TCP_STREAM_RAW_GET_PRIVATE (stream);
1233
G_DEFINE_QUARK (camel-proxy-error-quark, camel_proxy_error)
1236
* camel_tcp_stream_raw_new:
1238
* Create a new #CamelTcpStreamRaw object.
1240
* Returns: a new #CamelTcpStream object
1243
camel_tcp_stream_raw_new (void)
1245
return g_object_new (CAMEL_TYPE_TCP_STREAM_RAW, NULL);